Skip to content

Fix: Timezone dropdown label does not account for DST #584

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 125 additions & 67 deletions packages/timezone-data/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,272 +1,330 @@
const timezoneData: {name: string; label: string}[] = [
{
name: 'Pacific/Pago_Pago',
label: '(GMT -11:00) Midway Island, Samoa'
label: 'Midway Island, Samoa'
},
{
name: 'Pacific/Honolulu',
label: '(GMT -10:00) Hawaii'
label: 'Hawaii'
},
{
name: 'America/Anchorage',
label: '(GMT -9:00) Alaska'
label: 'Alaska'
},
{
name: 'America/Tijuana',
label: '(GMT -8:00) Chihuahua, La Paz, Mazatlan'
label: 'Chihuahua, La Paz, Mazatlan'
},
{
name: 'America/Los_Angeles',
label: '(GMT -8:00) Pacific Time (US & Canada); Tijuana'
label: 'Pacific Time (US & Canada); Tijuana'
},
{
name: 'America/Phoenix',
label: '(GMT -7:00) Arizona'
label: 'Arizona'
},
{
name: 'America/Denver',
label: '(GMT -7:00) Mountain Time (US & Canada)'
label: 'Mountain Time (US & Canada)'
},
{
name: 'America/Costa_Rica',
label: '(GMT -6:00) Central America'
label: 'Central America'
},
{
name: 'America/Chicago',
label: '(GMT -6:00) Central Time (US & Canada)'
label: 'Central Time (US & Canada)'
},
{
name: 'America/Mexico_City',
label: '(GMT -6:00) Guadalajara, Mexico City, Monterrey'
label: 'Guadalajara, Mexico City, Monterrey'
},
{
name: 'America/Regina',
label: '(GMT -6:00) Saskatchewan'
label: 'Saskatchewan'
},
{
name: 'America/Bogota',
label: '(GMT -5:00) Bogota, Lima, Quito'
label: 'Bogota, Lima, Quito'
},
{
name: 'America/New_York',
label: '(GMT -5:00) Eastern Time (US & Canada)'
label: 'Eastern Time (US & Canada)'
},
{
name: 'America/Fort_Wayne',
label: '(GMT -5:00) Indiana (East)'
label: 'Indiana (East)'
},
{
name: 'America/Caracas',
label: '(GMT -4:00) Caracas, La Paz'
label: 'Caracas, La Paz'
},
{
name: 'America/Halifax',
label: '(GMT -4:00) Atlantic Time (Canada); Greenland'
label: 'Atlantic Time (Canada); Greenland'
},
{
name: 'America/Santiago',
label: '(GMT -4:00) Santiago'
label: 'Santiago'
},
{
name: 'America/St_Johns',
label: '(GMT -3:30) Newfoundland'
label: 'Newfoundland'
},
{
name: 'America/Argentina/Buenos_Aires',
label: '(GMT -3:00) Buenos Aires, Brasilia, Georgetown'
label: 'Buenos Aires, Brasilia, Georgetown'
},
{
name: 'America/Noronha',
label: '(GMT -2:00) Fernando de Noronha'
label: 'Fernando de Noronha'
},
{
name: 'Atlantic/Azores',
label: '(GMT -1:00) Azores'
label: 'Azores'
},
{
name: 'Atlantic/Cape_Verde',
label: '(GMT -1:00) Cape Verde Is.'
label: 'Cape Verde Is.'
},
{
name: 'Etc/UTC',
label: '(GMT) UTC'
label: 'UTC'
},
{
name: 'Africa/Casablanca',
label: '(GMT +0:00) Casablanca, Monrovia'
label: 'Casablanca, Monrovia'
},
{
name: 'Europe/Dublin',
label: '(GMT +0:00) Dublin, Edinburgh, London'
label: 'Dublin, Edinburgh, London'
},
{
name: 'Europe/Amsterdam',
label: '(GMT +1:00) Amsterdam, Berlin, Rome, Stockholm, Vienna'
label: ' Amsterdam, Berlin, Rome, Stockholm, Vienna'
},
{
name: 'Europe/Prague',
label: '(GMT +1:00) Belgrade, Bratislava, Budapest, Prague'
label: 'Belgrade, Bratislava, Budapest, Prague'
},
{
name: 'Europe/Paris',
label: '(GMT +1:00) Brussels, Copenhagen, Madrid, Paris'
label: 'Brussels, Copenhagen, Madrid, Paris'
},
{
name: 'Europe/Warsaw',
label: '(GMT +1:00) Sarajevo, Skopje, Warsaw, Zagreb'
label: 'Sarajevo, Skopje, Warsaw, Zagreb'
},
{
name: 'Africa/Lagos',
label: '(GMT +1:00) West Central Africa'
label: 'West Central Africa'
},
{
name: 'Europe/Athens',
label: '(GMT +2:00) Athens, Beirut, Bucharest'
label: 'Athens, Beirut, Bucharest'
},
{
name: 'Africa/Cairo',
label: '(GMT +2:00) Cairo, Egypt'
label: 'Cairo, Egypt'
},
{
name: 'Africa/Maputo',
label: '(GMT +2:00) Harare'
label: 'Harare'
},
{
name: 'Europe/Kiev', // Changing name to 'Europe/Kiev', and keeping the UI with Kyiv. Change this once we are passed the moment lib update.
label: '(GMT +2:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius'
label: 'Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius'
},
{
name: 'Asia/Jerusalem',
label: '(GMT +2:00) Jerusalem'
label: 'Jerusalem'
},
{
name: 'Africa/Johannesburg',
label: '(GMT +2:00) Pretoria'
label: 'Pretoria'
},
{
name: 'Asia/Baghdad',
label: '(GMT +3:00) Baghdad'
label: 'Baghdad'
},
{
name: 'Asia/Riyadh',
label: '(GMT +3:00) Kuwait, Nairobi, Riyadh'
label: 'Kuwait, Nairobi, Riyadh'
},
{
name: 'Europe/Istanbul',
label: '(GMT +3:00) Istanbul, Ankara'
label: 'Istanbul, Ankara'
},
{
name: 'Europe/Moscow',
label: '(GMT +3:00) Moscow, St. Petersburg, Volgograd'
label: 'Moscow, St. Petersburg, Volgograd'
},
{
name: 'Asia/Tehran',
label: '(GMT +3:30) Tehran'
label: 'Tehran'
},
{
name: 'Asia/Dubai',
label: '(GMT +4:00) Abu Dhabi, Muscat'
label: 'Abu Dhabi, Muscat'
},
{
name: 'Asia/Baku',
label: '(GMT +4:00) Baku, Tbilisi, Yerevan'
label: 'Baku, Tbilisi, Yerevan'
},
{
name: 'Asia/Kabul',
label: '(GMT +4:30) Kabul'
label: 'Kabul'
},
{
name: 'Asia/Karachi',
label: '(GMT +5:00) Islamabad, Karachi, Tashkent'
label: 'Islamabad, Karachi, Tashkent'
},
{
name: 'Asia/Yekaterinburg',
label: '(GMT +5:00) Yekaterinburg'
label: 'Yekaterinburg'
},
{
name: 'Asia/Kolkata',
label: '(GMT +5:30) Chennai, Calcutta, Mumbai, New Delhi'
label: 'Chennai, Kolkata, Mumbai, New Delhi'
},
{
name: 'Asia/Kathmandu',
label: '(GMT +5:45) Katmandu'
label: 'Katmandu'
},
{
name: 'Asia/Almaty',
label: '(GMT +6:00) Almaty, Novosibirsk'
label: 'Almaty, Novosibirsk'
},
{
name: 'Asia/Dhaka',
label: '(GMT +6:00) Astana, Dhaka, Sri Jayawardenepura'
label: 'Astana, Dhaka, Sri Jayawardenepura'
},
{
name: 'Asia/Rangoon',
label: '(GMT +6:30) Rangoon'
label: 'Rangoon'
},
{
name: 'Asia/Bangkok',
label: '(GMT +7:00) Bangkok, Hanoi, Jakarta'
label: 'Bangkok, Hanoi, Jakarta'
},
{
name: 'Asia/Krasnoyarsk',
label: '(GMT +7:00) Krasnoyarsk'
label: 'Krasnoyarsk'
},
{
name: 'Asia/Hong_Kong',
label: '(GMT +8:00) Beijing, Chongqing, Hong Kong, Urumqi'
label: 'Beijing, Chongqing, Hong Kong, Urumqi'
},
{
name: 'Asia/Irkutsk',
label: '(GMT +8:00) Irkutsk, Ulaan Bataar'
label: 'Irkutsk, Ulaan Bataar'
},
{
name: 'Asia/Singapore',
label: '(GMT +8:00) Kuala Lumpur, Perth, Singapore, Taipei'
label: 'Kuala Lumpur, Perth, Singapore, Taipei'
},
{
name: 'Asia/Tokyo',
label: '(GMT +9:00) Osaka, Sapporo, Tokyo'
label: 'Osaka, Sapporo, Tokyo'
},
{
name: 'Asia/Seoul',
label: '(GMT +9:00) Seoul'
label: 'Seoul'
},
{
name: 'Asia/Yakutsk',
label: '(GMT +9:00) Yakutsk'
label: 'Yakutsk'
},
{
name: 'Australia/Adelaide',
label: '(GMT +9:30) Adelaide'
label: 'Adelaide'
},
{
name: 'Australia/Darwin',
label: '(GMT +9:30) Darwin'
label: 'Darwin'
},
{
name: 'Australia/Brisbane',
label: '(GMT +10:00) Brisbane, Guam, Port Moresby'
label: 'Brisbane, Guam, Port Moresby'
},
{
name: 'Australia/Sydney',
label: '(GMT +10:00) Canberra, Hobart, Melbourne, Sydney, Vladivostok'
label: 'Canberra, Hobart, Melbourne, Sydney, Vladivostok'
},
{
name: 'Asia/Magadan',
label: '(GMT +11:00) Magadan, Soloman Is., New Caledonia'
label: 'Magadan, Soloman Is., New Caledonia'
},
{
name: 'Pacific/Auckland',
label: '(GMT +12:00) Auckland, Wellington'
label: 'Auckland, Wellington'
},
{
name: 'Pacific/Fiji',
label: '(GMT +12:00) Fiji, Kamchatka, Marshall Is.'
label: 'Fiji, Kamchatka, Marshall Is.'
},
{
name: 'Pacific/Kwajalein',
label: '(GMT +12:00) International Date Line West'
label: 'International Date Line West'
}
];

interface GMTOffsetData {
offsetString: string | null;
offsetMinutes: number;
}

interface TimezoneDataWithOffset {
name: string;
label: string;
offsetValue: number;
}

export const getGMTOffset = (timeZone: string): GMTOffsetData => {
const options: Intl.DateTimeFormatOptions = {
timeZone,
timeZoneName: 'longOffset'
};

const formatter = new Intl.DateTimeFormat('en-GB', options);
const parts = formatter.formatToParts(new Date());
const offsetPart = parts.find(part => part.type === 'timeZoneName')?.value;

if (!offsetPart) {
return {offsetString: null, offsetMinutes: 0};
}

// Expecting formats like "GMT+05:30" or "GMT-08:00"
const match = offsetPart.match(/^GMT([+-])(\d{2}):(\d{2})$/);

if (!match) {
return {offsetString: offsetPart, offsetMinutes: 0};
}

const [, sign, hourStr, minuteStr] = match;
const hour = parseInt(hourStr, 10);
const minute = parseInt(minuteStr, 10);
const totalMinutes = sign === '+' ? (hour * 60 + minute) : -(hour * 60 + minute);
const offsetString = `GMT ${sign}${hour}:${minute.toString().padStart(2, '0')}`;

return {offsetString, offsetMinutes: totalMinutes};
};

const labelWithGMTOffset = (label: string, offsetString: string): string => {
return '(' + offsetString + ') ' + label;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could use a template literal here to stay consistent with L308

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, I've updated with this change.

};

export const timezoneDataWithGMTOffset = (): TimezoneDataWithOffset[] => {
return timezoneData
.map(({name, label}) => {
const {offsetString, offsetMinutes} = getGMTOffset(name);
return {
name,
label: offsetString ? labelWithGMTOffset(label, offsetString) : label,
offsetValue: offsetMinutes
};
})
.sort((a, b) => a.offsetValue - b.offsetValue);
};

export default timezoneData;
Loading