macOS: Add support for 12/24-hour time display preferences

This commit is contained in:
Jamie Kyle 2023-07-31 09:23:19 -07:00 committed by GitHub
parent 88858af144
commit 1143c0e9ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 208 additions and 14 deletions

View file

@ -1,15 +1,92 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { HourCyclePreference } from '../types/I18N';
import { assertDev } from './assert';
function getOptionsWithPreferences(
options: Intl.DateTimeFormatOptions
): Intl.DateTimeFormatOptions {
const hourCyclePreference = window.getHourCyclePreference();
if (options.hour12 != null) {
return options;
}
if (hourCyclePreference === HourCyclePreference.Prefer12) {
return { ...options, hour12: true };
}
if (hourCyclePreference === HourCyclePreference.Prefer24) {
return { ...options, hour12: false };
}
return options;
}
/**
* Chrome doesn't implement hour12 correctly
*/
function fixBuggyOptions(
locales: Array<string>,
options: Intl.DateTimeFormatOptions
): Intl.DateTimeFormatOptions {
const resolvedOptions = new Intl.DateTimeFormat(
locales,
options
).resolvedOptions();
const resolvedLocale = new Intl.Locale(resolvedOptions.locale);
let { hourCycle } = resolvedOptions;
// Most languages should use either h24 or h12
if (hourCycle === 'h24') {
hourCycle = 'h23';
}
if (hourCycle === 'h11') {
hourCycle = 'h12';
}
// Only Japanese should use h11 when using hour12 time
if (hourCycle === 'h12' && resolvedLocale.language === 'ja') {
hourCycle = 'h11';
}
return {
...options,
hour12: undefined,
hourCycle,
};
}
function getCacheKey(
locales: Array<string>,
options: Intl.DateTimeFormatOptions
) {
return `${locales.join(',')}:${Object.keys(options)
.sort()
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.map(key => `${key}=${(options as any)[key]}`)
.join(',')}`;
}
const formatterCache = new Map<string, Intl.DateTimeFormat>();
export function getDateTimeFormatter(
options: Intl.DateTimeFormatOptions
): Intl.DateTimeFormat {
const locales = window.getPreferredSystemLocales();
const optionsWithPreferences = getOptionsWithPreferences(options);
const cacheKey = getCacheKey(locales, optionsWithPreferences);
const cachedFormatter = formatterCache.get(cacheKey);
if (cachedFormatter) {
return cachedFormatter;
}
const fixedOptions = fixBuggyOptions(locales, optionsWithPreferences);
const formatter = new Intl.DateTimeFormat(locales, fixedOptions);
formatterCache.set(cacheKey, formatter);
return formatter;
}
export function formatTimestamp(
timestamp: number,
options: Intl.DateTimeFormatOptions
): string {
const locale = window.getPreferredSystemLocales();
const formatter = getDateTimeFormatter(options);
try {
return new Intl.DateTimeFormat(locale, options).format(timestamp);
return formatter.format(timestamp);
} catch (err) {
assertDev(false, 'invalid timestamp');
return '';