2

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?

William Stewart
  • 841
  • 1
  • 14
  • 27
  • May I recommend using jodatime instead of the Calendar object? – Kristy Welsh Jan 14 '15 at 17:50
  • Its not the Calendar object that I'm having problems with, just TimeZone at the moment. I can see jodatime has its own TimeZone implementation also, might be worth giving it a go. Cheers. – William Stewart Jan 14 '15 at 18:00
  • Joda time has actually saved me a lot of headaches and inherent errors in the Calendar object. Easy to use, understand and implement. – Kristy Welsh Jan 14 '15 at 18:07
  • 2
    I don't see how Joda-Time can help in this concrete case because [Joda-Time](http://www.joda.org/joda-time/apidocs/org/joda/time/DateTimeZone.html#getAvailableIDs()) will yield strings like "MST7MDT" or "Etc/...", too. – Meno Hochschild Jan 15 '15 at 17:44

1 Answers1

3

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. Some turned out to be an inadvertent duplicate of another zone. 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 classes 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?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154