MenoData / Time4J

Advanced date, time and interval library for Java with sun/moon-astronomy and calendars like Chinese, Coptic, Ethiopian, French Republican, Hebrew, Hijri, Historic Christian, Indian National, Japanese, Julian, Korean, Minguo, Persian, Thai, Vietnamese
GNU Lesser General Public License v2.1
440 stars 64 forks source link

Not a complete list of time zones and their aliases #756

Closed AlexKnyazyk closed 5 years ago

AlexKnyazyk commented 6 years ago

In our android project we use WinZoneProviderSPI.NAME_BASED_MAP for converting the iana timeszone to windows. But we are forced to use a crutch like this, because on some devices the timeszone is determined by its alias. Link on source Wiki. The first value is something that is not in the library but is an alias, the second value is what is.

Map<String, String> aliasMap = new HashMap<>(); aliasMap.put("Asia/Ho_Chi_Minh", "Asia/Saigon"); aliasMap.put("America/Kentucky/Louisville", "America/Louisville"); aliasMap.put("Pacific/Pohnpei", "Pacific/Ponape"); aliasMap.put("America/Atikokan", "America/Coral_Harbour"); aliasMap.put("America/Argentina/Buenos_Aires", "America/Buenos_Aires"); aliasMap.put("America/Indiana/Indianapolis", "America/Indianapolis"); aliasMap.put("America/Argentina/Jujuy", "America/Jujuy"); aliasMap.put("Asia/Kathmandu", "Asia/Katmandu"); aliasMap.put("Pacific/Chuuk", "Pacific/Truk"); aliasMap.put("America/Argentina/Mendoza", "America/Mendoza"); aliasMap.put("Atlantic/Faroe", "Atlantic/Faeroe"); aliasMap.put("Asia/Yangon", "Asia/Rangoon"); aliasMap.put("America/Argentina/Cordoba", "America/Cordoba"); aliasMap.put("Asia/Kolkata", "Asia/Calcutta"); aliasMap.put("America/Argentina/Catamarca", "America/Catamarca"); aliasMap.put("America/Argentina/ComodRivadavia", "America/Catamarca"); aliasMap.put("Europe/Tiraspol", "Europe/Chisinau"); aliasMap.put("America/Ensenada", "America/Tijuana"); aliasMap.put("America/Fort_Wayne", "America/Indianapolis"); aliasMap.put("America/Porto_Acre", "America/Rio_Branco"); aliasMap.put("Asia/Tel_Aviv", "Asia/Jerusalem"); aliasMap.put("Asia/Ulan_Bator", "Asia/Ulaanbaatar"); aliasMap.put("Antarctica/South_Pole", "Pacific/Auckland"); aliasMap.put("Asia/Kashgar", "Asia/Urumqi"); aliasMap.put("America/Rosario", "America/Cordoba"); aliasMap.put("Asia/Dacca", "Asia/Dhaka"); aliasMap.put("Australia/Yancowinna", "Australia/Broken_Hill"); aliasMap.put("Asia/Ashkhabad", "Asia/Ashgabat"); aliasMap.put("America/Virgin", "America/Port_of_Spain"); aliasMap.put("Asia/Harbin", "Asia/Shanghai"); aliasMap.put("Asia/Macao", "Asia/Macau"); aliasMap.put("Asia/Thimbu", "Asia/Thimphu"); aliasMap.put("Australia/Canberra", "Australia/Sydney"); aliasMap.put("Atlantic/Jan_Mayen", "Europe/Oslo"); aliasMap.put("Asia/Chungking", "Asia/Shanghai"); aliasMap.put("Asia/Chongqing", "Asia/Shanghai"); aliasMap.put("America/Atka", "America/Adak"); aliasMap.put("Europe/Belfast", "Europe/London"); aliasMap.put("Pacific/Yap", "Pacific/Truk"); aliasMap.put("Africa/Timbuktu", "Africa/Abidjan"); aliasMap.put("America/Shiprock", "America/Denver"); aliasMap.put("Asia/Ujung_Pandang", "Asia/Makassar");

MenoData commented 6 years ago

Well, the following test code shows that the aliases can be recognized. You see different timezone identifiers but exactly the same rules:

    Timezone.of("Asia/Yangon").dump(System.out);
    System.out.println("----------------------");
    Timezone.of("Asia/Rangoon").dump(System.out);
    System.out.println("----------------------");

Start Of Dump => *** Timezone-ID:

Asia/Yangon Strategy: net.time4j.tz.TransitionResolver:[gap=PUSH_FORWARD,overlap=LATER_OFFSET] History: Transition at: 1942-04-30T17:30:00Z from +06:30 to +09:00, DST=+00:00 Transition at: 1945-05-02T15:00:00Z from +09:00 to +06:30, DST=+00:00 <= End Of Dump

Start Of Dump => Timezone-ID: Asia/Rangoon Strategy: net.time4j.tz.TransitionResolver:[gap=PUSH_FORWARD,overlap=LATER_OFFSET] *** History: Transition at: 1942-04-30T17:30:00Z from +06:30 to +09:00, DST=+00:00 Transition at: 1945-05-02T15:00:00Z from +09:00 to +06:30, DST=+00:00 <= End Of Dump

Ah wait a moment, your question is about an internal detail of winzone-mapping. I will look into it.

MenoData commented 6 years ago

Actually, Time4J supports aliases in general but only in one direction, namely resolving aliases internally to the real IANA-identifiers. Similar direction is valid for windows-zone-identifiers.

The non-public-api-map you are actually using contains entries like:

Israel Standard Time={001=[WINDOWS~Asia/Jerusalem], IL=[WINDOWS~Asia/Jerusalem]}

This map is optimized for the purpose of look up of winzones in combination with a country-id and does not need to know about aliases like "Asia/Tel_Aviv" (in this direction). I have now following new methods for the reverse direction in mind:

class Timezone:

static TZID normalize(TZID) // resolves aliases to normal identifiers

class WindowsZone:

static WindowsZone from(TZID, Locale)

MenoData commented 6 years ago

Now I have released Time4A-v3.41 and the misc-module in version v3.41. Following features have been added so you should be able to solve your problem without accessing internal implementations:

Some notes about your alias map:

I appreciate any feedback very much if the new version works for you.

MenoData commented 6 years ago

Now I have even released the misc-module in the version v3.41.1 which contains an automatic fallback to territory "001" (the CLDR-symbol for "worldwide") if the windows zone name does not exist for a given country identifier. So your issue is pretty much solved (the tzdata-module which is necessary for normalizing any aliases is already included in the latest Time4A-version).

AlexKnyazyk commented 5 years ago

@MenoData Thanks, Your changes help me :) i use such solution: WindowsZone.toString(TimeZone.getDefault().getID(), Locale.ENGLISH); But i find some problem, i use 2 library dependencies:

implementation "net.time4j:time4j-android:4.3-2019a"
implementation ("net.time4j:time4j-misc:3.41.1") {
        exclude module: 'time4j-core'
        exclude module: 'time4j-i18n'
}

I first tried to use the latest version of the "misc" library 4.38, but it is not working with them :(

MenoData commented 5 years ago

Thanks for your feedback. I have already learnt that Android architecture is not optimized for a modular Time4A, so I think the winzone-classes should just be added to Time4A directly - without an extra aar-file. However, I should then look for an option how to register or unregister these additional tz-identifiers to the tz-repository of Time4A.

MenoData commented 5 years ago

Time4A with version 4.4 contains the win-zone-support, so only one dependency is needed (not yet released however):

implementation "net.time4j:time4j-android:4.4-2019a"

Example for working code in Time4A:

    try {
        TZID winzoneID = WindowsZone.of("Romance Standard Time").resolveSmart(Locale.FRANCE);
        WindowsZone.registerAsTimezone();
        String winzoneName = Timezone.of(winzoneID).getDisplayName(
                    NameStyle.LONG_STANDARD_TIME, 
                    Locale.FRANCE);
        Log.i("TIME4A", "Winzone: " + winzoneID.canonical() + "=>" + winzoneName);
    } catch (IllegalArgumentException ex) {
        Log.e("TIME4A", "Winzone not known.");
    }

Output: "Winzone: WINDOWS~Europe/Paris=>Romance Standard Time"

Attention: If you need expressions like Timezone.of(winzoneID) or want to parse names like "Romance Standard Time" in date-time-patterns using ChronoFormatter then you need to call WindowsZone.registerAsTimezone(). This can best be done directly after using ApplicationStarter in the initialization of Time4A.

MenoData commented 5 years ago

For your information: Time4A (4.4-2019a) has been released today.