macOS: Add support for 12/24-hour time display preferences
This commit is contained in:
parent
88858af144
commit
1143c0e9ba
17 changed files with 208 additions and 14 deletions
|
@ -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 '';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue