Android TimeZone.getAvailableIDs() producing strange strings

落爺英雄遲暮 提交于 2020-04-30 07:52:37

问题


I have an Android application which uses TimeZone.getAvailableIDs() to pull all available timezone ID strings. These IDs are saved to an ArrayList, as per the following:

ArrayList<String> clocks = new ArrayList<>();
String[] ids = TimeZone.getAvailableIDs();
Collections.addAll(clocks, ids);

This ArrayList is used later on to populate a ListView using my custom BaseAdapter. There are a few entries that aren't particularly informative and I don't want them in the list, such as:

Etc/GMT+10
PST8PDT
NZ-CHAT

I added a for loop to go through all entries and remove any unwanted ones, at the moment I have the following checks for testing purposes:

String string = clocks.get(i);
String[] split = string.split("/");

if(string.equals("MST7MDT")) {
    clocks.remove(i);
}

if(string.contains("Etc/")) {
    clocks.remove(i);
}

if(split.length <= 1) {
    clocks.remove(i);
}

if(!string.contains("/")) {
    clocks.remove(i);
}

Now this should delete the one identical to "MST7MDT" (it doesn't), any containing "Etc/" (only about half of the Etc/example elements get deleted), and any that don't contain a "/" the split one should do the same (not all are deleted). I've tried it with trim() but it hasn't helped. It doesn't seem to be a problem with remove(int), I tried passing the Object too but still nothing.

Any help would be much appreciated, has anyone else experienced problems with the results from TimeZone.getAvailableIDs()? Am I missing something stupid here?


回答1:


Keep zones, rather than discard

As we can see in the list of time zone names on Wikipedia, the zones have changed and evolved over the years. Many of the entries are mere alias for other zones. And many were ill-conceived and are now deprecated.

I suggest that rather than focusing on zones to delete, you focus on zones to keep. The rest of this Answer shows how.

TimeZone.getAvailableIDs()

The TimeZone class was years ago supplanted by the java.time.ZoneId class. Never use the terrible legacy date-time zones such as TimeZone, Date, and Calendar.

List of continents

As a first step in getting a cleaned-up list, I suggest filtering for those names that begin with one of these continents:

  • Europe
  • Africa
  • Antarctica
  • Atlantic
  • America
  • Pacific
  • Indian
  • Australia

And add an entry of Etc/UTC for when the user wants no time zone at all.

In Java code, sorted alphabetically.

List < String > zoneGroupNames = List.of(
        "Africa" ,
        "Antarctica" ,
        "Atlantic" ,
        "America" ,
        "Australia" ,
        "Europe" ,
        "Indian" ,
        "Pacific" ,
        "UTC"
);

I suggest you offer a two-step process for the user, where they suggest first a continent, then a zone.

Multimap of zone group name to zone names

Build a Map of each zone group name to collection of zone id names. We need a map of the group name such as Europe to a list of the zone names such as Europe/Berlin, Europe/London, and Europe/Malta.

Map < String, List < String > > mapGroupNameToZoneNames = new TreeMap <>();

Mapping a key to a collection of values is known as a "multimap". We now have built-in multimap functionality with the Map implementations bundled with Java. Call Map::computeIfAbsent (see this Answer).

Set < String > zoneIdStrings = ZoneId.getAvailableZoneIds();
for ( String zoneIdString : zoneIdStrings )
{
    String groupName = zoneIdString.split( "/" )[ 0 ];
    if ( zoneGroupNames.contains( groupName ) )
    {
        mapGroupNameToZoneNames.computeIfAbsent( groupName , ( x -> new ArrayList <>() ) ).add( zoneIdString );
    } // Else skip it.
}

System.out.println( "mapGroupNameToZoneNames = " + mapGroupNameToZoneNames );

Add that one entry for Etc/UTC.

mapGroupNameToZoneNames.computeIfAbsent( "Etc" , ( x -> new ArrayList <>() ) ).add( "UTC" );

Present to user

Present that list of groups to the user. Say the user selects item # 6 (index 5), which is currently Europe.

String groupNameChosenByUser = zoneGroupNames.get( 5 ); // Europe
List < String > zoneNamesOfGroup = mapGroupNameToZoneNames.get( groupNameChosenByUser );

Present that list of zone names for that one group. Say the user selects item # 12 (index 11), which is currently Europe/Malta.

String zoneNameChosenByUser = zoneNamesOfGroup.get( 11 );  // Malta

Make a ZoneId object from the string of that zone name.

ZoneId zoneIdChosenByUser = ZoneId.of( zoneNameChosenByUser );

zoneIdChosenByUser.toString() = Europe/Malta


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

  • Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and Java SE 7
    • Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android
    • Later versions of Android bundle implementations of the java.time classes.
    • For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….


来源:https://stackoverflow.com/questions/27949230/android-timezone-getavailableids-producing-strange-strings

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!