mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
## Problem The dashboard renders all timestamps in the browser's local timezone. When debugging app issues, users often want to see logs and timestamps in a different timezone (e.g. their app's deployment region) without changing their OS clock. ## Fix - New Timezone submenu in the user-avatar dropdown, sitting next to the existing Theme picker. Search-as-you-type combobox over the full IANA catalog plus an Auto detect option. - Selection persists in localStorage (`supabase-ui-timezone`) and survives `clearLocalStorage()`. No backend schema change. - New `lib/datetime.tsx` exposes pure timezone-aware formatters (`formatDateTime`, `formatDate`, `formatTime`, `formatFromNow`, `toTimezone`) plus a `TimezoneProvider` and matching React hooks (`useTimezone`, `useFormatDateTime`, ...). The pure functions take `tz` explicitly so they're easy to unit test (17 vitest cases covering DST transitions, multi-tz formatting, unix-micro/Date inputs, invalid-tz fallback). - The selected timezone propagates to every existing `<TimestampInfo>` in Studio via a new `TimestampInfoProvider` context exported from `ui-patterns`. No per-callsite changes needed for those ~20+ surfaces. - The `UnifiedLogs` date column migrates off `date-fns` to the new `useFormatDateTime` hook (the rest of the date-fns callers stay as-is, since they're either internal range math or non-display). - `ALL_TIMEZONES` (~600 entries) moves out of `PITR.constants.ts` into a shared `lib/constants/timezones.ts`. PITR keeps a re-export shim so its callers don't move. New `TIMEZONES_BY_IANA` dedupes the catalog by primary IANA name (the original list contains both PDT and PST rows for `America/Los_Angeles`, etc.) and `findTimezoneByIana` provides reverse lookup. - Telemetry: `timezone_picker_clicked` PostHog event with `previousTimezone`, `nextTimezone`, `isAutoDetected` properties. Notes for reviewers: - Bare `dayjs(x).format(...)` calls (~157 files) intentionally still render in browser-local time. Surfaces opt in by switching to the new wrappers, so this PR is the abstraction plus logs adoption; broader migration is a follow-up. - Two `// prettier-ignore` lines (`apps/studio/pages/_app.tsx`, `apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.fields.tsx`) work around a pre-existing local-tooling issue where `prettier-plugin-sql-cst` strips angle-bracket type arguments under certain conditions. Project's pinned prettier (3.8.1) does not strip; the issue surfaces with a globally-installed prettier. Worth tracking separately. - Hydration: `guessLocalTimezone()` and `useLocalStorageQuery` are client-only. Studio is mostly CSR via the Pages Router, but any SSR'd `<TimestampInfo>` may briefly render in the server's tz before client hydration. Existing behavior already had this mismatch with `.local()`; this PR does not regress it. - Backend timestamps round-tripped through query params and mutations stay UTC. The picker is display-only. ## How to test - Run `pnpm dev:studio`, sign in. - Open the user avatar dropdown (top right). Hover Timezone. - Search for "tokyo", pick `(UTC+09:00) Osaka, Sapporo, Tokyo`. - Open any project, navigate to Logs (e.g. `Project > Logs > Edge Functions`). Hover a log row's timestamp; the popover should show UTC, the chosen tz (`Asia/Tokyo`), and the relative time. Visible cell text should be in JST. - Visit any page that uses `<TimestampInfo>` (Database > Backups, Project Pause state, Edge Function details). Same tooltip should reflect Asia/Tokyo. - Refresh the page; timezone is still Asia/Tokyo. - Reopen the picker, choose Auto detect; timestamps revert to browser local. - Run `pnpm --filter studio test lib/datetime.test.ts`. 17 tests should pass. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Timezone selector added to the user menu with auto-detect and manual override * App-wide timezone provider and hooks plus a shared timezone catalog for consistent timezone-aware display * Timestamp components accept an optional timezone prop and respect user preference (persisted) * **Bug Fixes / Improvements** * Logs and timestamp displays now use the new timezone formatting hooks * **Tests** * Added comprehensive datetime and timezone catalog tests * **Telemetry** * Telemetry event added for timezone picker interactions <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1222 lines
27 KiB
TypeScript
1222 lines
27 KiB
TypeScript
// https://github.com/dmfilipenko/timezones.json/blob/master/timezones.json
|
|
export interface Timezone {
|
|
value: string
|
|
abbr: string
|
|
offset: number
|
|
isdst: boolean
|
|
text: string
|
|
utc: string[]
|
|
}
|
|
|
|
export const ALL_TIMEZONES: Timezone[] = [
|
|
{
|
|
value: 'Dateline Standard Time',
|
|
abbr: 'DST',
|
|
offset: -12,
|
|
isdst: false,
|
|
text: '(UTC-12:00) International Date Line West',
|
|
utc: ['Etc/GMT+12'],
|
|
},
|
|
{
|
|
value: 'UTC-11',
|
|
abbr: 'U',
|
|
offset: -11,
|
|
isdst: false,
|
|
text: '(UTC-11:00) Coordinated Universal Time-11',
|
|
utc: ['Etc/GMT+11', 'Pacific/Midway', 'Pacific/Niue', 'Pacific/Pago_Pago'],
|
|
},
|
|
{
|
|
value: 'Hawaiian Standard Time',
|
|
abbr: 'HST',
|
|
offset: -10,
|
|
isdst: false,
|
|
text: '(UTC-10:00) Hawaii',
|
|
utc: [
|
|
'Etc/GMT+10',
|
|
'Pacific/Honolulu',
|
|
'Pacific/Johnston',
|
|
'Pacific/Rarotonga',
|
|
'Pacific/Tahiti',
|
|
],
|
|
},
|
|
{
|
|
value: 'Alaskan Standard Time',
|
|
abbr: 'AKDT',
|
|
offset: -8,
|
|
isdst: true,
|
|
text: '(UTC-09:00) Alaska',
|
|
utc: [
|
|
'America/Anchorage',
|
|
'America/Juneau',
|
|
'America/Nome',
|
|
'America/Sitka',
|
|
'America/Yakutat',
|
|
],
|
|
},
|
|
{
|
|
value: 'Pacific Standard Time (Mexico)',
|
|
abbr: 'PDT',
|
|
offset: -7,
|
|
isdst: true,
|
|
text: '(UTC-08:00) Baja California',
|
|
utc: ['America/Santa_Isabel'],
|
|
},
|
|
{
|
|
value: 'Pacific Daylight Time',
|
|
abbr: 'PDT',
|
|
offset: -7,
|
|
isdst: true,
|
|
text: '(UTC-07:00) Pacific Daylight Time (US & Canada)',
|
|
utc: ['America/Los_Angeles', 'America/Tijuana', 'America/Vancouver'],
|
|
},
|
|
{
|
|
value: 'Pacific Standard Time',
|
|
abbr: 'PST',
|
|
offset: -8,
|
|
isdst: false,
|
|
text: '(UTC-08:00) Pacific Standard Time (US & Canada)',
|
|
utc: ['America/Los_Angeles', 'America/Tijuana', 'America/Vancouver', 'PST8PDT'],
|
|
},
|
|
{
|
|
value: 'US Mountain Standard Time',
|
|
abbr: 'UMST',
|
|
offset: -7,
|
|
isdst: false,
|
|
text: '(UTC-07:00) Arizona',
|
|
utc: [
|
|
'America/Creston',
|
|
'America/Dawson',
|
|
'America/Dawson_Creek',
|
|
'America/Hermosillo',
|
|
'America/Phoenix',
|
|
'America/Whitehorse',
|
|
'Etc/GMT+7',
|
|
],
|
|
},
|
|
{
|
|
value: 'Mountain Standard Time (Mexico)',
|
|
abbr: 'MDT',
|
|
offset: -6,
|
|
isdst: true,
|
|
text: '(UTC-07:00) Chihuahua, La Paz, Mazatlan',
|
|
utc: ['America/Chihuahua', 'America/Mazatlan'],
|
|
},
|
|
{
|
|
value: 'Mountain Standard Time',
|
|
abbr: 'MDT',
|
|
offset: -6,
|
|
isdst: true,
|
|
text: '(UTC-07:00) Mountain Time (US & Canada)',
|
|
utc: [
|
|
'America/Boise',
|
|
'America/Cambridge_Bay',
|
|
'America/Denver',
|
|
'America/Edmonton',
|
|
'America/Inuvik',
|
|
'America/Ojinaga',
|
|
'America/Yellowknife',
|
|
'MST7MDT',
|
|
],
|
|
},
|
|
{
|
|
value: 'Central America Standard Time',
|
|
abbr: 'CAST',
|
|
offset: -6,
|
|
isdst: false,
|
|
text: '(UTC-06:00) Central America',
|
|
utc: [
|
|
'America/Belize',
|
|
'America/Costa_Rica',
|
|
'America/El_Salvador',
|
|
'America/Guatemala',
|
|
'America/Managua',
|
|
'America/Tegucigalpa',
|
|
'Etc/GMT+6',
|
|
'Pacific/Galapagos',
|
|
],
|
|
},
|
|
{
|
|
value: 'Central Standard Time',
|
|
abbr: 'CDT',
|
|
offset: -5,
|
|
isdst: true,
|
|
text: '(UTC-06:00) Central Time (US & Canada)',
|
|
utc: [
|
|
'America/Chicago',
|
|
'America/Indiana/Knox',
|
|
'America/Indiana/Tell_City',
|
|
'America/Matamoros',
|
|
'America/Menominee',
|
|
'America/North_Dakota/Beulah',
|
|
'America/North_Dakota/Center',
|
|
'America/North_Dakota/New_Salem',
|
|
'America/Rainy_River',
|
|
'America/Rankin_Inlet',
|
|
'America/Resolute',
|
|
'America/Winnipeg',
|
|
'CST6CDT',
|
|
],
|
|
},
|
|
{
|
|
value: 'Central Standard Time (Mexico)',
|
|
abbr: 'CDT',
|
|
offset: -5,
|
|
isdst: true,
|
|
text: '(UTC-06:00) Guadalajara, Mexico City, Monterrey',
|
|
utc: [
|
|
'America/Bahia_Banderas',
|
|
'America/Cancun',
|
|
'America/Merida',
|
|
'America/Mexico_City',
|
|
'America/Monterrey',
|
|
],
|
|
},
|
|
{
|
|
value: 'Canada Central Standard Time',
|
|
abbr: 'CCST',
|
|
offset: -6,
|
|
isdst: false,
|
|
text: '(UTC-06:00) Saskatchewan',
|
|
utc: ['America/Regina', 'America/Swift_Current'],
|
|
},
|
|
{
|
|
value: 'SA Pacific Standard Time',
|
|
abbr: 'SPST',
|
|
offset: -5,
|
|
isdst: false,
|
|
text: '(UTC-05:00) Bogota, Lima, Quito',
|
|
utc: [
|
|
'America/Bogota',
|
|
'America/Cayman',
|
|
'America/Coral_Harbour',
|
|
'America/Eirunepe',
|
|
'America/Guayaquil',
|
|
'America/Jamaica',
|
|
'America/Lima',
|
|
'America/Panama',
|
|
'America/Rio_Branco',
|
|
'Etc/GMT+5',
|
|
],
|
|
},
|
|
{
|
|
value: 'Eastern Standard Time',
|
|
abbr: 'EST',
|
|
offset: -5,
|
|
isdst: false,
|
|
text: '(UTC-05:00) Eastern Time (US & Canada)',
|
|
utc: [
|
|
'America/Detroit',
|
|
'America/Havana',
|
|
'America/Indiana/Petersburg',
|
|
'America/Indiana/Vincennes',
|
|
'America/Indiana/Winamac',
|
|
'America/Iqaluit',
|
|
'America/Kentucky/Monticello',
|
|
'America/Louisville',
|
|
'America/Montreal',
|
|
'America/Nassau',
|
|
'America/New_York',
|
|
'America/Nipigon',
|
|
'America/Pangnirtung',
|
|
'America/Port-au-Prince',
|
|
'America/Thunder_Bay',
|
|
'America/Toronto',
|
|
],
|
|
},
|
|
{
|
|
value: 'Eastern Daylight Time',
|
|
abbr: 'EDT',
|
|
offset: -4,
|
|
isdst: true,
|
|
text: '(UTC-04:00) Eastern Daylight Time (US & Canada)',
|
|
utc: [
|
|
'America/Detroit',
|
|
'America/Havana',
|
|
'America/Indiana/Petersburg',
|
|
'America/Indiana/Vincennes',
|
|
'America/Indiana/Winamac',
|
|
'America/Iqaluit',
|
|
'America/Kentucky/Monticello',
|
|
'America/Louisville',
|
|
'America/Montreal',
|
|
'America/Nassau',
|
|
'America/New_York',
|
|
'America/Nipigon',
|
|
'America/Pangnirtung',
|
|
'America/Port-au-Prince',
|
|
'America/Thunder_Bay',
|
|
'America/Toronto',
|
|
],
|
|
},
|
|
{
|
|
value: 'US Eastern Standard Time',
|
|
abbr: 'UEDT',
|
|
offset: -5,
|
|
isdst: false,
|
|
text: '(UTC-05:00) Indiana (East)',
|
|
utc: ['America/Indiana/Marengo', 'America/Indiana/Vevay', 'America/Indianapolis'],
|
|
},
|
|
{
|
|
value: 'Venezuela Standard Time',
|
|
abbr: 'VST',
|
|
offset: -4.5,
|
|
isdst: false,
|
|
text: '(UTC-04:30) Caracas',
|
|
utc: ['America/Caracas'],
|
|
},
|
|
{
|
|
value: 'Paraguay Standard Time',
|
|
abbr: 'PYT',
|
|
offset: -4,
|
|
isdst: false,
|
|
text: '(UTC-04:00) Asuncion',
|
|
utc: ['America/Asuncion'],
|
|
},
|
|
{
|
|
value: 'Atlantic Standard Time',
|
|
abbr: 'ADT',
|
|
offset: -3,
|
|
isdst: true,
|
|
text: '(UTC-04:00) Atlantic Time (Canada)',
|
|
utc: [
|
|
'America/Glace_Bay',
|
|
'America/Goose_Bay',
|
|
'America/Halifax',
|
|
'America/Moncton',
|
|
'America/Thule',
|
|
'Atlantic/Bermuda',
|
|
],
|
|
},
|
|
{
|
|
value: 'Central Brazilian Standard Time',
|
|
abbr: 'CBST',
|
|
offset: -4,
|
|
isdst: false,
|
|
text: '(UTC-04:00) Cuiaba',
|
|
utc: ['America/Campo_Grande', 'America/Cuiaba'],
|
|
},
|
|
{
|
|
value: 'SA Western Standard Time',
|
|
abbr: 'SWST',
|
|
offset: -4,
|
|
isdst: false,
|
|
text: '(UTC-04:00) Georgetown, La Paz, Manaus, San Juan',
|
|
utc: [
|
|
'America/Anguilla',
|
|
'America/Antigua',
|
|
'America/Aruba',
|
|
'America/Barbados',
|
|
'America/Blanc-Sablon',
|
|
'America/Boa_Vista',
|
|
'America/Curacao',
|
|
'America/Dominica',
|
|
'America/Grand_Turk',
|
|
'America/Grenada',
|
|
'America/Guadeloupe',
|
|
'America/Guyana',
|
|
'America/Kralendijk',
|
|
'America/La_Paz',
|
|
'America/Lower_Princes',
|
|
'America/Manaus',
|
|
'America/Marigot',
|
|
'America/Martinique',
|
|
'America/Montserrat',
|
|
'America/Port_of_Spain',
|
|
'America/Porto_Velho',
|
|
'America/Puerto_Rico',
|
|
'America/Santo_Domingo',
|
|
'America/St_Barthelemy',
|
|
'America/St_Kitts',
|
|
'America/St_Lucia',
|
|
'America/St_Thomas',
|
|
'America/St_Vincent',
|
|
'America/Tortola',
|
|
'Etc/GMT+4',
|
|
],
|
|
},
|
|
{
|
|
value: 'Pacific SA Standard Time',
|
|
abbr: 'PSST',
|
|
offset: -4,
|
|
isdst: false,
|
|
text: '(UTC-04:00) Santiago',
|
|
utc: ['America/Santiago', 'Antarctica/Palmer'],
|
|
},
|
|
{
|
|
value: 'Newfoundland Standard Time',
|
|
abbr: 'NDT',
|
|
offset: -2.5,
|
|
isdst: true,
|
|
text: '(UTC-03:30) Newfoundland',
|
|
utc: ['America/St_Johns'],
|
|
},
|
|
{
|
|
value: 'E. South America Standard Time',
|
|
abbr: 'ESAST',
|
|
offset: -3,
|
|
isdst: false,
|
|
text: '(UTC-03:00) Brasilia',
|
|
utc: ['America/Sao_Paulo'],
|
|
},
|
|
{
|
|
value: 'Argentina Standard Time',
|
|
abbr: 'AST',
|
|
offset: -3,
|
|
isdst: false,
|
|
text: '(UTC-03:00) Buenos Aires',
|
|
utc: [
|
|
'America/Argentina/La_Rioja',
|
|
'America/Argentina/Rio_Gallegos',
|
|
'America/Argentina/Salta',
|
|
'America/Argentina/San_Juan',
|
|
'America/Argentina/San_Luis',
|
|
'America/Argentina/Tucuman',
|
|
'America/Argentina/Ushuaia',
|
|
'America/Buenos_Aires',
|
|
'America/Catamarca',
|
|
'America/Cordoba',
|
|
'America/Jujuy',
|
|
'America/Mendoza',
|
|
],
|
|
},
|
|
{
|
|
value: 'SA Eastern Standard Time',
|
|
abbr: 'SEST',
|
|
offset: -3,
|
|
isdst: false,
|
|
text: '(UTC-03:00) Cayenne, Fortaleza',
|
|
utc: [
|
|
'America/Araguaina',
|
|
'America/Belem',
|
|
'America/Cayenne',
|
|
'America/Fortaleza',
|
|
'America/Maceio',
|
|
'America/Paramaribo',
|
|
'America/Recife',
|
|
'America/Santarem',
|
|
'Antarctica/Rothera',
|
|
'Atlantic/Stanley',
|
|
'Etc/GMT+3',
|
|
],
|
|
},
|
|
{
|
|
value: 'Greenland Standard Time',
|
|
abbr: 'GDT',
|
|
offset: -3,
|
|
isdst: true,
|
|
text: '(UTC-03:00) Greenland',
|
|
utc: ['America/Godthab'],
|
|
},
|
|
{
|
|
value: 'Montevideo Standard Time',
|
|
abbr: 'MST',
|
|
offset: -3,
|
|
isdst: false,
|
|
text: '(UTC-03:00) Montevideo',
|
|
utc: ['America/Montevideo'],
|
|
},
|
|
{
|
|
value: 'Bahia Standard Time',
|
|
abbr: 'BST',
|
|
offset: -3,
|
|
isdst: false,
|
|
text: '(UTC-03:00) Salvador',
|
|
utc: ['America/Bahia'],
|
|
},
|
|
{
|
|
value: 'UTC-02',
|
|
abbr: 'U',
|
|
offset: -2,
|
|
isdst: false,
|
|
text: '(UTC-02:00) Coordinated Universal Time-02',
|
|
utc: ['America/Noronha', 'Atlantic/South_Georgia', 'Etc/GMT+2'],
|
|
},
|
|
{
|
|
value: 'Azores Standard Time',
|
|
abbr: 'ADT',
|
|
offset: 0,
|
|
isdst: true,
|
|
text: '(UTC-01:00) Azores',
|
|
utc: ['America/Scoresbysund', 'Atlantic/Azores'],
|
|
},
|
|
{
|
|
value: 'Cape Verde Standard Time',
|
|
abbr: 'CVST',
|
|
offset: -1,
|
|
isdst: false,
|
|
text: '(UTC-01:00) Cape Verde Is.',
|
|
utc: ['Atlantic/Cape_Verde', 'Etc/GMT+1'],
|
|
},
|
|
{
|
|
value: 'Morocco Standard Time',
|
|
abbr: 'MDT',
|
|
offset: 1,
|
|
isdst: true,
|
|
text: '(UTC) Casablanca',
|
|
utc: ['Africa/Casablanca', 'Africa/El_Aaiun'],
|
|
},
|
|
{
|
|
value: 'UTC',
|
|
abbr: 'UTC',
|
|
offset: 0,
|
|
isdst: false,
|
|
text: '(UTC) Coordinated Universal Time',
|
|
utc: ['America/Danmarkshavn', 'Etc/GMT'],
|
|
},
|
|
{
|
|
value: 'British Summer Time',
|
|
abbr: 'BST',
|
|
offset: 1,
|
|
isdst: true,
|
|
text: '(UTC+01:00) Edinburgh, London',
|
|
utc: ['Europe/Isle_of_Man', 'Europe/Guernsey', 'Europe/Jersey', 'Europe/London'],
|
|
},
|
|
{
|
|
value: 'GMT Standard Time',
|
|
abbr: 'GDT',
|
|
offset: 1,
|
|
isdst: true,
|
|
text: '(UTC) Dublin, Lisbon',
|
|
utc: [
|
|
'Atlantic/Canary',
|
|
'Atlantic/Faeroe',
|
|
'Atlantic/Madeira',
|
|
'Europe/Dublin',
|
|
'Europe/Lisbon',
|
|
],
|
|
},
|
|
{
|
|
value: 'Greenwich Standard Time',
|
|
abbr: 'GST',
|
|
offset: 0,
|
|
isdst: false,
|
|
text: '(UTC) Monrovia, Reykjavik',
|
|
utc: [
|
|
'Africa/Abidjan',
|
|
'Africa/Accra',
|
|
'Africa/Bamako',
|
|
'Africa/Banjul',
|
|
'Africa/Bissau',
|
|
'Africa/Conakry',
|
|
'Africa/Dakar',
|
|
'Africa/Freetown',
|
|
'Africa/Lome',
|
|
'Africa/Monrovia',
|
|
'Africa/Nouakchott',
|
|
'Africa/Ouagadougou',
|
|
'Africa/Sao_Tome',
|
|
'Atlantic/Reykjavik',
|
|
'Atlantic/St_Helena',
|
|
],
|
|
},
|
|
{
|
|
value: 'W. Europe Standard Time',
|
|
abbr: 'WEDT',
|
|
offset: 2,
|
|
isdst: true,
|
|
text: '(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna',
|
|
utc: [
|
|
'Arctic/Longyearbyen',
|
|
'Europe/Amsterdam',
|
|
'Europe/Andorra',
|
|
'Europe/Berlin',
|
|
'Europe/Busingen',
|
|
'Europe/Gibraltar',
|
|
'Europe/Luxembourg',
|
|
'Europe/Malta',
|
|
'Europe/Monaco',
|
|
'Europe/Oslo',
|
|
'Europe/Rome',
|
|
'Europe/San_Marino',
|
|
'Europe/Stockholm',
|
|
'Europe/Vaduz',
|
|
'Europe/Vatican',
|
|
'Europe/Vienna',
|
|
'Europe/Zurich',
|
|
],
|
|
},
|
|
{
|
|
value: 'Central Europe Standard Time',
|
|
abbr: 'CEDT',
|
|
offset: 2,
|
|
isdst: true,
|
|
text: '(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague',
|
|
utc: [
|
|
'Europe/Belgrade',
|
|
'Europe/Bratislava',
|
|
'Europe/Budapest',
|
|
'Europe/Ljubljana',
|
|
'Europe/Podgorica',
|
|
'Europe/Prague',
|
|
'Europe/Tirane',
|
|
],
|
|
},
|
|
{
|
|
value: 'Romance Standard Time',
|
|
abbr: 'RDT',
|
|
offset: 2,
|
|
isdst: true,
|
|
text: '(UTC+01:00) Brussels, Copenhagen, Madrid, Paris',
|
|
utc: ['Africa/Ceuta', 'Europe/Brussels', 'Europe/Copenhagen', 'Europe/Madrid', 'Europe/Paris'],
|
|
},
|
|
{
|
|
value: 'Central European Standard Time',
|
|
abbr: 'CEDT',
|
|
offset: 2,
|
|
isdst: true,
|
|
text: '(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb',
|
|
utc: ['Europe/Sarajevo', 'Europe/Skopje', 'Europe/Warsaw', 'Europe/Zagreb'],
|
|
},
|
|
{
|
|
value: 'W. Central Africa Standard Time',
|
|
abbr: 'WCAST',
|
|
offset: 1,
|
|
isdst: false,
|
|
text: '(UTC+01:00) West Central Africa',
|
|
utc: [
|
|
'Africa/Algiers',
|
|
'Africa/Bangui',
|
|
'Africa/Brazzaville',
|
|
'Africa/Douala',
|
|
'Africa/Kinshasa',
|
|
'Africa/Lagos',
|
|
'Africa/Libreville',
|
|
'Africa/Luanda',
|
|
'Africa/Malabo',
|
|
'Africa/Ndjamena',
|
|
'Africa/Niamey',
|
|
'Africa/Porto-Novo',
|
|
'Africa/Tunis',
|
|
'Etc/GMT-1',
|
|
],
|
|
},
|
|
{
|
|
value: 'Namibia Standard Time',
|
|
abbr: 'NST',
|
|
offset: 1,
|
|
isdst: false,
|
|
text: '(UTC+01:00) Windhoek',
|
|
utc: ['Africa/Windhoek'],
|
|
},
|
|
{
|
|
value: 'GTB Standard Time',
|
|
abbr: 'GDT',
|
|
offset: 3,
|
|
isdst: true,
|
|
text: '(UTC+02:00) Athens, Bucharest',
|
|
utc: ['Asia/Nicosia', 'Europe/Athens', 'Europe/Bucharest', 'Europe/Chisinau'],
|
|
},
|
|
{
|
|
value: 'Middle East Standard Time',
|
|
abbr: 'MEDT',
|
|
offset: 3,
|
|
isdst: true,
|
|
text: '(UTC+02:00) Beirut',
|
|
utc: ['Asia/Beirut'],
|
|
},
|
|
{
|
|
value: 'Egypt Standard Time',
|
|
abbr: 'EST',
|
|
offset: 2,
|
|
isdst: false,
|
|
text: '(UTC+02:00) Cairo',
|
|
utc: ['Africa/Cairo'],
|
|
},
|
|
{
|
|
value: 'Syria Standard Time',
|
|
abbr: 'SDT',
|
|
offset: 3,
|
|
isdst: true,
|
|
text: '(UTC+02:00) Damascus',
|
|
utc: ['Asia/Damascus'],
|
|
},
|
|
{
|
|
value: 'E. Europe Standard Time',
|
|
abbr: 'EEDT',
|
|
offset: 3,
|
|
isdst: true,
|
|
text: '(UTC+02:00) E. Europe',
|
|
utc: [
|
|
'Asia/Nicosia',
|
|
'Europe/Athens',
|
|
'Europe/Bucharest',
|
|
'Europe/Chisinau',
|
|
'Europe/Helsinki',
|
|
'Europe/Kiev',
|
|
'Europe/Mariehamn',
|
|
'Europe/Nicosia',
|
|
'Europe/Riga',
|
|
'Europe/Sofia',
|
|
'Europe/Tallinn',
|
|
'Europe/Uzhgorod',
|
|
'Europe/Vilnius',
|
|
'Europe/Zaporozhye',
|
|
],
|
|
},
|
|
{
|
|
value: 'South Africa Standard Time',
|
|
abbr: 'SAST',
|
|
offset: 2,
|
|
isdst: false,
|
|
text: '(UTC+02:00) Harare, Pretoria',
|
|
utc: [
|
|
'Africa/Blantyre',
|
|
'Africa/Bujumbura',
|
|
'Africa/Gaborone',
|
|
'Africa/Harare',
|
|
'Africa/Johannesburg',
|
|
'Africa/Kigali',
|
|
'Africa/Lubumbashi',
|
|
'Africa/Lusaka',
|
|
'Africa/Maputo',
|
|
'Africa/Maseru',
|
|
'Africa/Mbabane',
|
|
'Etc/GMT-2',
|
|
],
|
|
},
|
|
{
|
|
value: 'FLE Standard Time',
|
|
abbr: 'FDT',
|
|
offset: 3,
|
|
isdst: true,
|
|
text: '(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius',
|
|
utc: [
|
|
'Europe/Helsinki',
|
|
'Europe/Kiev',
|
|
'Europe/Mariehamn',
|
|
'Europe/Riga',
|
|
'Europe/Sofia',
|
|
'Europe/Tallinn',
|
|
'Europe/Uzhgorod',
|
|
'Europe/Vilnius',
|
|
'Europe/Zaporozhye',
|
|
],
|
|
},
|
|
{
|
|
value: 'Turkey Standard Time',
|
|
abbr: 'TDT',
|
|
offset: 3,
|
|
isdst: false,
|
|
text: '(UTC+03:00) Istanbul',
|
|
utc: ['Europe/Istanbul'],
|
|
},
|
|
{
|
|
value: 'Israel Standard Time',
|
|
abbr: 'JDT',
|
|
offset: 3,
|
|
isdst: true,
|
|
text: '(UTC+02:00) Jerusalem',
|
|
utc: ['Asia/Jerusalem'],
|
|
},
|
|
{
|
|
value: 'Libya Standard Time',
|
|
abbr: 'LST',
|
|
offset: 2,
|
|
isdst: false,
|
|
text: '(UTC+02:00) Tripoli',
|
|
utc: ['Africa/Tripoli'],
|
|
},
|
|
{
|
|
value: 'Jordan Standard Time',
|
|
abbr: 'JST',
|
|
offset: 3,
|
|
isdst: false,
|
|
text: '(UTC+03:00) Amman',
|
|
utc: ['Asia/Amman'],
|
|
},
|
|
{
|
|
value: 'Arabic Standard Time',
|
|
abbr: 'AST',
|
|
offset: 3,
|
|
isdst: false,
|
|
text: '(UTC+03:00) Baghdad',
|
|
utc: ['Asia/Baghdad'],
|
|
},
|
|
{
|
|
value: 'Kaliningrad Standard Time',
|
|
abbr: 'KST',
|
|
offset: 3,
|
|
isdst: false,
|
|
text: '(UTC+02:00) Kaliningrad',
|
|
utc: ['Europe/Kaliningrad'],
|
|
},
|
|
{
|
|
value: 'Arab Standard Time',
|
|
abbr: 'AST',
|
|
offset: 3,
|
|
isdst: false,
|
|
text: '(UTC+03:00) Kuwait, Riyadh',
|
|
utc: ['Asia/Aden', 'Asia/Bahrain', 'Asia/Kuwait', 'Asia/Qatar', 'Asia/Riyadh'],
|
|
},
|
|
{
|
|
value: 'E. Africa Standard Time',
|
|
abbr: 'EAST',
|
|
offset: 3,
|
|
isdst: false,
|
|
text: '(UTC+03:00) Nairobi',
|
|
utc: [
|
|
'Africa/Addis_Ababa',
|
|
'Africa/Asmera',
|
|
'Africa/Dar_es_Salaam',
|
|
'Africa/Djibouti',
|
|
'Africa/Juba',
|
|
'Africa/Kampala',
|
|
'Africa/Khartoum',
|
|
'Africa/Mogadishu',
|
|
'Africa/Nairobi',
|
|
'Antarctica/Syowa',
|
|
'Etc/GMT-3',
|
|
'Indian/Antananarivo',
|
|
'Indian/Comoro',
|
|
'Indian/Mayotte',
|
|
],
|
|
},
|
|
{
|
|
value: 'Moscow Standard Time',
|
|
abbr: 'MSK',
|
|
offset: 3,
|
|
isdst: false,
|
|
text: '(UTC+03:00) Moscow, St. Petersburg, Volgograd, Minsk',
|
|
utc: ['Europe/Kirov', 'Europe/Moscow', 'Europe/Simferopol', 'Europe/Volgograd', 'Europe/Minsk'],
|
|
},
|
|
{
|
|
value: 'Samara Time',
|
|
abbr: 'SAMT',
|
|
offset: 4,
|
|
isdst: false,
|
|
text: '(UTC+04:00) Samara, Ulyanovsk, Saratov',
|
|
utc: ['Europe/Astrakhan', 'Europe/Samara', 'Europe/Ulyanovsk'],
|
|
},
|
|
{
|
|
value: 'Iran Standard Time',
|
|
abbr: 'IDT',
|
|
offset: 4.5,
|
|
isdst: true,
|
|
text: '(UTC+03:30) Tehran',
|
|
utc: ['Asia/Tehran'],
|
|
},
|
|
{
|
|
value: 'Arabian Standard Time',
|
|
abbr: 'AST',
|
|
offset: 4,
|
|
isdst: false,
|
|
text: '(UTC+04:00) Abu Dhabi, Muscat',
|
|
utc: ['Asia/Dubai', 'Asia/Muscat', 'Etc/GMT-4'],
|
|
},
|
|
{
|
|
value: 'Azerbaijan Standard Time',
|
|
abbr: 'ADT',
|
|
offset: 5,
|
|
isdst: true,
|
|
text: '(UTC+04:00) Baku',
|
|
utc: ['Asia/Baku'],
|
|
},
|
|
{
|
|
value: 'Mauritius Standard Time',
|
|
abbr: 'MST',
|
|
offset: 4,
|
|
isdst: false,
|
|
text: '(UTC+04:00) Port Louis',
|
|
utc: ['Indian/Mahe', 'Indian/Mauritius', 'Indian/Reunion'],
|
|
},
|
|
{
|
|
value: 'Georgian Standard Time',
|
|
abbr: 'GET',
|
|
offset: 4,
|
|
isdst: false,
|
|
text: '(UTC+04:00) Tbilisi',
|
|
utc: ['Asia/Tbilisi'],
|
|
},
|
|
{
|
|
value: 'Caucasus Standard Time',
|
|
abbr: 'CST',
|
|
offset: 4,
|
|
isdst: false,
|
|
text: '(UTC+04:00) Yerevan',
|
|
utc: ['Asia/Yerevan'],
|
|
},
|
|
{
|
|
value: 'Afghanistan Standard Time',
|
|
abbr: 'AST',
|
|
offset: 4.5,
|
|
isdst: false,
|
|
text: '(UTC+04:30) Kabul',
|
|
utc: ['Asia/Kabul'],
|
|
},
|
|
{
|
|
value: 'West Asia Standard Time',
|
|
abbr: 'WAST',
|
|
offset: 5,
|
|
isdst: false,
|
|
text: '(UTC+05:00) Ashgabat, Tashkent',
|
|
utc: [
|
|
'Antarctica/Mawson',
|
|
'Asia/Aqtau',
|
|
'Asia/Aqtobe',
|
|
'Asia/Ashgabat',
|
|
'Asia/Dushanbe',
|
|
'Asia/Oral',
|
|
'Asia/Samarkand',
|
|
'Asia/Tashkent',
|
|
'Etc/GMT-5',
|
|
'Indian/Kerguelen',
|
|
'Indian/Maldives',
|
|
],
|
|
},
|
|
{
|
|
value: 'Yekaterinburg Time',
|
|
abbr: 'YEKT',
|
|
offset: 5,
|
|
isdst: false,
|
|
text: '(UTC+05:00) Yekaterinburg',
|
|
utc: ['Asia/Yekaterinburg'],
|
|
},
|
|
{
|
|
value: 'Pakistan Standard Time',
|
|
abbr: 'PKT',
|
|
offset: 5,
|
|
isdst: false,
|
|
text: '(UTC+05:00) Islamabad, Karachi',
|
|
utc: ['Asia/Karachi'],
|
|
},
|
|
{
|
|
value: 'India Standard Time',
|
|
abbr: 'IST',
|
|
offset: 5.5,
|
|
isdst: false,
|
|
text: '(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi',
|
|
utc: ['Asia/Kolkata', 'Asia/Calcutta'],
|
|
},
|
|
{
|
|
value: 'Sri Lanka Standard Time',
|
|
abbr: 'SLST',
|
|
offset: 5.5,
|
|
isdst: false,
|
|
text: '(UTC+05:30) Sri Jayawardenepura',
|
|
utc: ['Asia/Colombo'],
|
|
},
|
|
{
|
|
value: 'Nepal Standard Time',
|
|
abbr: 'NST',
|
|
offset: 5.75,
|
|
isdst: false,
|
|
text: '(UTC+05:45) Kathmandu',
|
|
utc: ['Asia/Kathmandu'],
|
|
},
|
|
{
|
|
value: 'Central Asia Standard Time',
|
|
abbr: 'CAST',
|
|
offset: 6,
|
|
isdst: false,
|
|
text: '(UTC+06:00) Nur-Sultan (Astana)',
|
|
utc: [
|
|
'Antarctica/Vostok',
|
|
'Asia/Almaty',
|
|
'Asia/Bishkek',
|
|
'Asia/Qyzylorda',
|
|
'Asia/Urumqi',
|
|
'Etc/GMT-6',
|
|
'Indian/Chagos',
|
|
],
|
|
},
|
|
{
|
|
value: 'Bangladesh Standard Time',
|
|
abbr: 'BST',
|
|
offset: 6,
|
|
isdst: false,
|
|
text: '(UTC+06:00) Dhaka',
|
|
utc: ['Asia/Dhaka', 'Asia/Thimphu'],
|
|
},
|
|
{
|
|
value: 'Myanmar Standard Time',
|
|
abbr: 'MST',
|
|
offset: 6.5,
|
|
isdst: false,
|
|
text: '(UTC+06:30) Yangon (Rangoon)',
|
|
utc: ['Asia/Rangoon', 'Indian/Cocos'],
|
|
},
|
|
{
|
|
value: 'SE Asia Standard Time',
|
|
abbr: 'SAST',
|
|
offset: 7,
|
|
isdst: false,
|
|
text: '(UTC+07:00) Bangkok, Hanoi, Jakarta',
|
|
utc: [
|
|
'Antarctica/Davis',
|
|
'Asia/Bangkok',
|
|
'Asia/Hovd',
|
|
'Asia/Jakarta',
|
|
'Asia/Phnom_Penh',
|
|
'Asia/Pontianak',
|
|
'Asia/Saigon',
|
|
'Asia/Vientiane',
|
|
'Etc/GMT-7',
|
|
'Indian/Christmas',
|
|
],
|
|
},
|
|
{
|
|
value: 'N. Central Asia Standard Time',
|
|
abbr: 'NCAST',
|
|
offset: 7,
|
|
isdst: false,
|
|
text: '(UTC+07:00) Novosibirsk',
|
|
utc: ['Asia/Novokuznetsk', 'Asia/Novosibirsk', 'Asia/Omsk'],
|
|
},
|
|
{
|
|
value: 'China Standard Time',
|
|
abbr: 'CST',
|
|
offset: 8,
|
|
isdst: false,
|
|
text: '(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi',
|
|
utc: ['Asia/Hong_Kong', 'Asia/Macau', 'Asia/Shanghai'],
|
|
},
|
|
{
|
|
value: 'North Asia Standard Time',
|
|
abbr: 'NAST',
|
|
offset: 8,
|
|
isdst: false,
|
|
text: '(UTC+08:00) Krasnoyarsk',
|
|
utc: ['Asia/Krasnoyarsk'],
|
|
},
|
|
{
|
|
value: 'Singapore Standard Time',
|
|
abbr: 'MPST',
|
|
offset: 8,
|
|
isdst: false,
|
|
text: '(UTC+08:00) Kuala Lumpur, Singapore',
|
|
utc: [
|
|
'Asia/Brunei',
|
|
'Asia/Kuala_Lumpur',
|
|
'Asia/Kuching',
|
|
'Asia/Makassar',
|
|
'Asia/Manila',
|
|
'Asia/Singapore',
|
|
'Etc/GMT-8',
|
|
],
|
|
},
|
|
{
|
|
value: 'W. Australia Standard Time',
|
|
abbr: 'WAST',
|
|
offset: 8,
|
|
isdst: false,
|
|
text: '(UTC+08:00) Perth',
|
|
utc: ['Antarctica/Casey', 'Australia/Perth'],
|
|
},
|
|
{
|
|
value: 'Taipei Standard Time',
|
|
abbr: 'TST',
|
|
offset: 8,
|
|
isdst: false,
|
|
text: '(UTC+08:00) Taipei',
|
|
utc: ['Asia/Taipei'],
|
|
},
|
|
{
|
|
value: 'Ulaanbaatar Standard Time',
|
|
abbr: 'UST',
|
|
offset: 8,
|
|
isdst: false,
|
|
text: '(UTC+08:00) Ulaanbaatar',
|
|
utc: ['Asia/Choibalsan', 'Asia/Ulaanbaatar'],
|
|
},
|
|
{
|
|
value: 'North Asia East Standard Time',
|
|
abbr: 'NAEST',
|
|
offset: 8,
|
|
isdst: false,
|
|
text: '(UTC+08:00) Irkutsk',
|
|
utc: ['Asia/Irkutsk'],
|
|
},
|
|
{
|
|
value: 'Japan Standard Time',
|
|
abbr: 'JST',
|
|
offset: 9,
|
|
isdst: false,
|
|
text: '(UTC+09:00) Osaka, Sapporo, Tokyo',
|
|
utc: ['Asia/Dili', 'Asia/Jayapura', 'Asia/Tokyo', 'Etc/GMT-9', 'Pacific/Palau'],
|
|
},
|
|
{
|
|
value: 'Korea Standard Time',
|
|
abbr: 'KST',
|
|
offset: 9,
|
|
isdst: false,
|
|
text: '(UTC+09:00) Seoul',
|
|
utc: ['Asia/Pyongyang', 'Asia/Seoul'],
|
|
},
|
|
{
|
|
value: 'Cen. Australia Standard Time',
|
|
abbr: 'CAST',
|
|
offset: 9.5,
|
|
isdst: false,
|
|
text: '(UTC+09:30) Adelaide',
|
|
utc: ['Australia/Adelaide', 'Australia/Broken_Hill'],
|
|
},
|
|
{
|
|
value: 'AUS Central Standard Time',
|
|
abbr: 'ACST',
|
|
offset: 9.5,
|
|
isdst: false,
|
|
text: '(UTC+09:30) Darwin',
|
|
utc: ['Australia/Darwin'],
|
|
},
|
|
{
|
|
value: 'E. Australia Standard Time',
|
|
abbr: 'EAST',
|
|
offset: 10,
|
|
isdst: false,
|
|
text: '(UTC+10:00) Brisbane',
|
|
utc: ['Australia/Brisbane', 'Australia/Lindeman'],
|
|
},
|
|
{
|
|
value: 'AUS Eastern Standard Time',
|
|
abbr: 'AEST',
|
|
offset: 10,
|
|
isdst: false,
|
|
text: '(UTC+10:00) Canberra, Melbourne, Sydney',
|
|
utc: ['Australia/Melbourne', 'Australia/Sydney'],
|
|
},
|
|
{
|
|
value: 'West Pacific Standard Time',
|
|
abbr: 'WPST',
|
|
offset: 10,
|
|
isdst: false,
|
|
text: '(UTC+10:00) Guam, Port Moresby',
|
|
utc: [
|
|
'Antarctica/DumontDUrville',
|
|
'Etc/GMT-10',
|
|
'Pacific/Guam',
|
|
'Pacific/Port_Moresby',
|
|
'Pacific/Saipan',
|
|
'Pacific/Truk',
|
|
],
|
|
},
|
|
{
|
|
value: 'Tasmania Standard Time',
|
|
abbr: 'TST',
|
|
offset: 10,
|
|
isdst: false,
|
|
text: '(UTC+10:00) Hobart',
|
|
utc: ['Australia/Currie', 'Australia/Hobart'],
|
|
},
|
|
{
|
|
value: 'Yakutsk Standard Time',
|
|
abbr: 'YST',
|
|
offset: 9,
|
|
isdst: false,
|
|
text: '(UTC+09:00) Yakutsk',
|
|
utc: ['Asia/Chita', 'Asia/Khandyga', 'Asia/Yakutsk'],
|
|
},
|
|
{
|
|
value: 'Central Pacific Standard Time',
|
|
abbr: 'CPST',
|
|
offset: 11,
|
|
isdst: false,
|
|
text: '(UTC+11:00) Solomon Is., New Caledonia',
|
|
utc: [
|
|
'Antarctica/Macquarie',
|
|
'Etc/GMT-11',
|
|
'Pacific/Efate',
|
|
'Pacific/Guadalcanal',
|
|
'Pacific/Kosrae',
|
|
'Pacific/Noumea',
|
|
'Pacific/Ponape',
|
|
],
|
|
},
|
|
{
|
|
value: 'Vladivostok Standard Time',
|
|
abbr: 'VST',
|
|
offset: 11,
|
|
isdst: false,
|
|
text: '(UTC+11:00) Vladivostok',
|
|
utc: ['Asia/Sakhalin', 'Asia/Ust-Nera', 'Asia/Vladivostok'],
|
|
},
|
|
{
|
|
value: 'New Zealand Standard Time',
|
|
abbr: 'NZST',
|
|
offset: 12,
|
|
isdst: false,
|
|
text: '(UTC+12:00) Auckland, Wellington',
|
|
utc: ['Antarctica/McMurdo', 'Pacific/Auckland'],
|
|
},
|
|
{
|
|
value: 'UTC+12',
|
|
abbr: 'U',
|
|
offset: 12,
|
|
isdst: false,
|
|
text: '(UTC+12:00) Coordinated Universal Time+12',
|
|
utc: [
|
|
'Etc/GMT-12',
|
|
'Pacific/Funafuti',
|
|
'Pacific/Kwajalein',
|
|
'Pacific/Majuro',
|
|
'Pacific/Nauru',
|
|
'Pacific/Tarawa',
|
|
'Pacific/Wake',
|
|
'Pacific/Wallis',
|
|
],
|
|
},
|
|
{
|
|
value: 'Fiji Standard Time',
|
|
abbr: 'FST',
|
|
offset: 12,
|
|
isdst: false,
|
|
text: '(UTC+12:00) Fiji',
|
|
utc: ['Pacific/Fiji'],
|
|
},
|
|
{
|
|
value: 'Magadan Standard Time',
|
|
abbr: 'MST',
|
|
offset: 12,
|
|
isdst: false,
|
|
text: '(UTC+12:00) Magadan',
|
|
utc: ['Asia/Anadyr', 'Asia/Kamchatka', 'Asia/Magadan', 'Asia/Srednekolymsk'],
|
|
},
|
|
{
|
|
value: 'Kamchatka Standard Time',
|
|
abbr: 'KDT',
|
|
offset: 13,
|
|
isdst: true,
|
|
text: '(UTC+12:00) Petropavlovsk-Kamchatsky - Old',
|
|
utc: ['Asia/Kamchatka'],
|
|
},
|
|
{
|
|
value: 'Tonga Standard Time',
|
|
abbr: 'TST',
|
|
offset: 13,
|
|
isdst: false,
|
|
text: "(UTC+13:00) Nuku'alofa",
|
|
utc: ['Etc/GMT-13', 'Pacific/Enderbury', 'Pacific/Fakaofo', 'Pacific/Tongatapu'],
|
|
},
|
|
{
|
|
value: 'Samoa Standard Time',
|
|
abbr: 'SST',
|
|
offset: 13,
|
|
isdst: false,
|
|
text: '(UTC+13:00) Samoa',
|
|
utc: ['Pacific/Apia'],
|
|
},
|
|
]
|
|
|
|
/**
|
|
* Deduplicated view of `ALL_TIMEZONES`, keyed by the entry's primary IANA
|
|
* name (`utc[0]`). Several catalog rows share an IANA target — e.g. PDT and
|
|
* PST both point to `America/Los_Angeles` — which would render duplicate UI
|
|
* rows that all store the same value when picked. We prefer the standard-time
|
|
* (`isdst: false`) row when both are present.
|
|
*/
|
|
export const TIMEZONES_BY_IANA: Timezone[] = (() => {
|
|
const seen = new Map<string, Timezone>()
|
|
for (const entry of ALL_TIMEZONES) {
|
|
const key = entry.utc[0]
|
|
if (!key) continue
|
|
const existing = seen.get(key)
|
|
if (!existing || (existing.isdst && !entry.isdst)) {
|
|
seen.set(key, entry)
|
|
}
|
|
}
|
|
return Array.from(seen.values())
|
|
})()
|
|
|
|
/** Look up the catalog entry that owns the given IANA name (any of `utc[]`). */
|
|
export const findTimezoneByIana = (iana: string): Timezone | undefined =>
|
|
ALL_TIMEZONES.find((entry) => entry.utc.includes(iana))
|