Skip to content
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

Support for deprecated timezone names? #44

Closed
MaybeThisIsRu opened this issue Aug 20, 2020 · 10 comments
Closed

Support for deprecated timezone names? #44

MaybeThisIsRu opened this issue Aug 20, 2020 · 10 comments

Comments

@MaybeThisIsRu
Copy link

MaybeThisIsRu commented Aug 20, 2020

I live in a time zone which has a deprecated name, Asia/Calcutta, the newer one being Asia/Kolkata.

While Firefox correctly reports Asia/Kolkata using the Internationalization API, Chromium based browsers do not:

Intl.DateTimeFormat().resolvedOptions().timeZone

On Chromium: Asia/Calcutta
On Firefox: Asia/Kolkata

There is a pending issue on the Chromium bug tracker 580195. Another issue 962806 was marked as duplicate, merged into 645807, which was ultimately closed without fixing this timezone.

The ideal scenario is that the Chromium team fixes this, of course. The issue has been open since January 2016 for Asia/Kolkata, and for better spec compliance in general since August 2018. At the moment, we do not know for certain how many timezones are affected with a similar bug -- which is why I come here with this request.

In the interim, is adding support for perhaps another key in the timeZone object, such as, backzones or whatever you see fit -- to include older names of the timezones something you would consider? Then developers are able to match with the backzones and use the matching canonical names for that backzone while saving to database.

Just as a quick example from the readme:

[
  // ...
  {
    name: "America/Los_Angeles",
    alternativeName: "Pacific Time",
    previousNames: ["Asia/Calcutta"],
    group: ["America/Los_Angeles"],
    countryName: "United States",
    mainCities: ["Los Angeles", "San Diego"],
    rawOffsetInMinutes: -480, // "raw" time zone offset, when there's no DST in place
    rawFormat: "-08:00 Pacific Time - Los Angeles, San Diego",
    currentTimeOffsetInMinutes: -420, // "current" time zone offset, this is why getTimeZones() is a method and not just an object: it can only work at runtime
    currentTimeFormat: "-07:00 Pacific Time - Los Angeles, San Diego",
  },
  // ...
];
@vvo
Copy link
Owner

vvo commented Aug 20, 2020

Hi there, thanks for the well-detailed issue.

I am reluctant on doing manual changes to the time zones because I fear the changes would be no more valid at some point. The files are generated for now.

There's one place where we do manual manipulation of data:

const alternativeNameCorrections = {

Still, we can try to find a way to solve your issue, I see two ways:

  • we find a generated list of previous time zones names that we can use in our generate.js script. So we can add all the previous names automatically. That would be ideal (we use http://download.geonames.org/export/dump/ data for now, but I think there's also https://github.com/rogierschouten/tzdata-generate#readme that could help here)
  • for now, you fix this on your side, by doing a .map() after calling getTimeZones() and adding this "missing" (previous name) timeZone name yourself

Let me know what you think!

@MaybeThisIsRu
Copy link
Author

MaybeThisIsRu commented Aug 20, 2020

I am also reluctant to handle this manually. I am building an app that may be used by users from any time zone and my goal is a good out-of-the-box experience. People also generally won't file issues for their time zone with me simply because they might not be technical users.

I could not find a clearly laid out data for this in the geonames.org dump -- though I'm sure I see some wildly linked data there which does signal old names of cities, if not timezones. Either way, I don't think that helps.

The second repository you linked to however has this data in its global tzdata file (but not continent specific tzdata files):

"Africa/Asmera": "Africa/Nairobi",
"Africa/Timbuktu": "Africa/Abidjan",
"America/Argentina/ComodRivadavia": "America/Argentina/Catamarca",
"America/Atka": "America/Adak",
"America/Buenos_Aires": "America/Argentina/Buenos_Aires",
"America/Catamarca": "America/Argentina/Catamarca",
"America/Coral_Harbour": "America/Atikokan",
"America/Cordoba": "America/Argentina/Cordoba",
"America/Ensenada": "America/Tijuana",
"America/Fort_Wayne": "America/Indiana/Indianapolis",
"America/Godthab": "America/Nuuk",
"America/Indianapolis": "America/Indiana/Indianapolis",
"America/Jujuy": "America/Argentina/Jujuy",
"America/Knox_IN": "America/Indiana/Knox",
"America/Louisville": "America/Kentucky/Louisville",
"America/Mendoza": "America/Argentina/Mendoza",
"America/Montreal": "America/Toronto",
"America/Porto_Acre": "America/Rio_Branco",
"America/Rosario": "America/Argentina/Cordoba",
"America/Santa_Isabel": "America/Tijuana",
"America/Shiprock": "America/Denver",
"America/Virgin": "America/Port_of_Spain",
"Antarctica/South_Pole": "Pacific/Auckland",
"Asia/Ashkhabad": "Asia/Ashgabat",
"Asia/Calcutta": "Asia/Kolkata",
"Asia/Chongqing": "Asia/Shanghai",
"Asia/Chungking": "Asia/Shanghai",
"Asia/Dacca": "Asia/Dhaka",
"Asia/Harbin": "Asia/Shanghai",
"Asia/Kashgar": "Asia/Urumqi",
"Asia/Katmandu": "Asia/Kathmandu",
"Asia/Macao": "Asia/Macau",
"Asia/Rangoon": "Asia/Yangon",
"Asia/Saigon": "Asia/Ho_Chi_Minh",
"Asia/Tel_Aviv": "Asia/Jerusalem",
"Asia/Thimbu": "Asia/Thimphu",
"Asia/Ujung_Pandang": "Asia/Makassar",
"Asia/Ulan_Bator": "Asia/Ulaanbaatar",
"Atlantic/Faeroe": "Atlantic/Faroe",
"Atlantic/Jan_Mayen": "Europe/Oslo",
"Australia/ACT": "Australia/Sydney",
"Australia/Canberra": "Australia/Sydney",
"Australia/LHI": "Australia/Lord_Howe",
"Australia/NSW": "Australia/Sydney",
"Australia/North": "Australia/Darwin",
"Australia/Queensland": "Australia/Brisbane",
"Australia/South": "Australia/Adelaide",
"Australia/Tasmania": "Australia/Hobart",
"Australia/Victoria": "Australia/Melbourne",
"Australia/West": "Australia/Perth",
"Australia/Yancowinna": "Australia/Broken_Hill",
"Brazil/Acre": "America/Rio_Branco",
"Brazil/DeNoronha": "America/Noronha",
"Brazil/East": "America/Sao_Paulo",
"Brazil/West": "America/Manaus",
"Canada/Atlantic": "America/Halifax",
"Canada/Central": "America/Winnipeg",
"Canada/Eastern": "America/Toronto",
"Canada/Mountain": "America/Edmonton",
"Canada/Newfoundland": "America/St_Johns",
"Canada/Pacific": "America/Vancouver",
"Canada/Saskatchewan": "America/Regina",
"Canada/Yukon": "America/Whitehorse",
"Chile/Continental": "America/Santiago",
"Chile/EasterIsland": "Pacific/Easter",
"Cuba": "America/Havana",
"Egypt": "Africa/Cairo",
"Eire": "Europe/Dublin",
"Etc/UCT": "Etc/UTC",
"Europe/Belfast": "Europe/London",
"Europe/Tiraspol": "Europe/Chisinau",
"GB": "Europe/London",
"GB-Eire": "Europe/London",
"GMT+0": "Etc/GMT",
"GMT-0": "Etc/GMT",
"GMT0": "Etc/GMT",
"Greenwich": "Etc/GMT",
"Hongkong": "Asia/Hong_Kong",
"Iceland": "Atlantic/Reykjavik",
"Iran": "Asia/Tehran",
"Israel": "Asia/Jerusalem",
"Jamaica": "America/Jamaica",
"Japan": "Asia/Tokyo",
"Kwajalein": "Pacific/Kwajalein",
"Libya": "Africa/Tripoli",
"Mexico/BajaNorte": "America/Tijuana",
"Mexico/BajaSur": "America/Mazatlan",
"Mexico/General": "America/Mexico_City",
"NZ": "Pacific/Auckland",
"NZ-CHAT": "Pacific/Chatham",
"Navajo": "America/Denver",
"PRC": "Asia/Shanghai",
"Pacific/Johnston": "Pacific/Honolulu",
"Pacific/Ponape": "Pacific/Pohnpei",
"Pacific/Samoa": "Pacific/Pago_Pago",
"Pacific/Truk": "Pacific/Chuuk",
"Pacific/Yap": "Pacific/Chuuk",
"Poland": "Europe/Warsaw",
"Portugal": "Europe/Lisbon",
"ROC": "Asia/Taipei",
"ROK": "Asia/Seoul",
"Singapore": "Asia/Singapore",
"Turkey": "Europe/Istanbul",
"UCT": "Etc/UTC",
"US/Alaska": "America/Anchorage",
"US/Aleutian": "America/Adak",
"US/Arizona": "America/Phoenix",
"US/Central": "America/Chicago",
"US/East-Indiana": "America/Indiana/Indianapolis",
"US/Eastern": "America/New_York",
"US/Hawaii": "Pacific/Honolulu",
"US/Indiana-Starke": "America/Indiana/Knox",
"US/Michigan": "America/Detroit",
"US/Mountain": "America/Denver",
"US/Pacific": "America/Los_Angeles",
"US/Samoa": "Pacific/Pago_Pago",
"UTC": "Etc/UTC",
"Universal": "Etc/UTC",
"W-SU": "Europe/Moscow",
"Zulu": "Etc/UTC",

I could not find the key under which this falls. It doesn't appear to be under its own key, in fact. Given that, I am not sure how we could programmatically extract this piece of data from the whole file. Do you think this is something we can move forward with?

@vvo
Copy link
Owner

vvo commented Aug 20, 2020

I could not find the key under which this falls. It doesn't appear to be under its own key, in fact. Given that, I am not sure how we could programmatically extract this piece of data from the whole file.

I think we're on the right path, using this file we could:

  • download it in generate.js
  • keep only the array entries (filter) where: key is a string, value is a string. This seems to be representing the "aliases/renamed" timezones
  • find all the keys where the value is one of the grouped timezones
  • add all found "old" time zone names directly inside the "group" property, I believe it should be safe to do so. And it should be done here:

    tzdb/generate.js

    Lines 203 to 207 in da39b58

    const group = uniq(
    timeZoneWithCities.map(({ timeZoneName }) => {
    return timeZoneName;
    }),
    );
    (concatenate both arrays)

Let me know how it goes! You can run the generate.js script locally (use latest Node v14) and see what are the changes. Thanks

@MaybeThisIsRu
Copy link
Author

I will attempt it next week and let you know how it goes. 👍

@steelbrain
Copy link

Fyi for people looking for a dump of alternative names, I scraped Wikipedia page of https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

Using this script
/* eslint-disable no-lonely-if */

;(function () {
    const tzsCanonical = []
    const tzsReferences = {}

    function findParent(source, callback) {
        let current = source
        do {
            if (callback(current)) {
                return current
            }
            current = current.parentElement
        } while (current != null)

        return null
    }

    const tzNameColumn = 2 // zero-indexed
    const tzStatusColumn = 4 // zero-indexed
    const tzNotesColumn = 7 // zero-indexed
    const randomElement = document.querySelector('[href="/wiki/America/New_York"]')
    const tzTable = findParent(randomElement, el => el.tagName === 'TBODY')

    Array.from(tzTable.children).forEach(tr => {
        const name = tr.children[tzNameColumn].textContent.trim()
        const status = tr.children[tzStatusColumn].textContent.trim()
        const notes = tr.children[tzNotesColumn]

        if (!name.includes('/') || name.startsWith('Etc/')) {
            // Ignore irrelevant ones
            return
        }

        if (status === 'Canonical') {
            // is original
            tzsCanonical.push(name)
        } else {
            // is an alias or Deprecated

            if (notes.textContent.trim().startsWith('Link to')) {
                // Links to another
                tzsReferences[name] = notes.querySelector('a').textContent.trim()
            } else {
                // Unknown item
                console.log(name, status, notes.textContent)
            }
        }
    })

    const tzDataMap = {}
    tzsCanonical.forEach(item => {
        tzDataMap[item] = {
            name: item,
            alternativeNames: [],
        }
    })
    Object.keys(tzsReferences).map(tzsReference => {
        if (tzsReferences[tzsReference] in tzDataMap) {
            tzDataMap[tzsReferences[tzsReference]].alternativeNames.push(tzsReference)
        } else {
            console.log('Source not found', tzsReference, tzsReferences[tzsReference])
        }
    })

    const tzData = Object.values(tzDataMap)
    console.log('tzData Ref', tzData)

    console.log(JSON.stringify(tzData))
})()

Here's the snapshot of output: https://gist.github.com/steelbrain/f1548e51d8d1e231d81b8609c6140f95

@KevinNovak
Copy link

Would love to have this built in, or perhaps an option on the constructor to include deprecated names or not?

@vvo vvo added the pr-welcome label Mar 19, 2021
@KevinNovak
Copy link

I made a comment here in #153 about how a list of deprecated time zones could be downloaded directly from the source, IANA Time Zone Database.

@vvo vvo closed this as completed in b27a341 Apr 14, 2021
@vvo
Copy link
Owner

vvo commented Apr 14, 2021

🎉 This issue has been resolved in version 6.7.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@vvo vvo added the released label Apr 14, 2021
@MaybeThisIsRu
Copy link
Author

MaybeThisIsRu commented Apr 14, 2021

So stoked to see this come out! 🎉

Sorry I couldn't help more, @vvo. 😔

@vvo
Copy link
Owner

vvo commented Apr 14, 2021

@hirusi Oh, you helped a lot already by opening the issue and adding comments on how to do it just like @KevinNovak helped also.

The thing is, unless I have a similar problem in production, I usually don't do new features and let people do them instead.

But this time I had the issue so I fixed it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants