Files
glchat/web/src/lib/timezones.ts
T

69 lines
2.3 KiB
TypeScript
Raw Normal View History

/**
* Часовые пояса для настроек профиля и раздела «Язык и регион».
*
* Значение — идентификатор IANA, как его принимает сервер; подпись собирается
* из названия зоны и текущего смещения (смещение считает `Intl`, поэтому
* переход на летнее время учитывается автоматически).
*/
/** Пояс по умолчанию: Москва (МСК). */
export const DEFAULT_TIMEZONE = 'Europe/Moscow';
export const timezoneOptions = [
'Europe/Kaliningrad',
'Europe/Moscow',
'Europe/Samara',
'Asia/Yekaterinburg',
'Asia/Almaty',
'Asia/Tashkent',
'Asia/Novosibirsk',
'Asia/Krasnoyarsk',
'Asia/Irkutsk',
'Asia/Yakutsk',
'Asia/Vladivostok',
'Asia/Magadan',
'Asia/Kamchatka',
'Europe/London',
'Europe/Berlin',
'Europe/Paris',
'Asia/Tbilisi',
'Asia/Yerevan',
'Asia/Dubai',
'Asia/Tokyo',
'Asia/Shanghai',
'America/New_York',
'America/Los_Angeles',
'UTC',
] as const;
/** Смещение вида `UTC+03:00` (для зоны, которой нет в ICU, — пустая строка). */
export function timezoneOffset(timeZone: string, locale = 'ru'): string {
try {
const parts = new Intl.DateTimeFormat(locale, {
timeZone,
timeZoneName: 'shortOffset',
}).formatToParts(new Date());
return parts.find((part) => part.type === 'timeZoneName')?.value ?? '';
} catch {
return '';
}
}
/** Подпись пункта списка: `Europe/Moscow (UTC+03:00)`. */
export function timezoneLabel(timeZone: string, locale = 'ru'): string {
const offset = timezoneOffset(timeZone, locale);
return offset === '' ? timeZone : `${timeZone} (${offset})`;
}
/**
* Все зоны списка плюс значение из профиля, если его там ещё нет: селект не
* должен «терять» часовой пояс, выставленный с другого клиента.
*/
export function timezoneChoices(value: string): string[] {
const options: string[] = [...timezoneOptions];
if (value !== '' && !options.includes(value)) {
options.unshift(value);
}
return options;
}