-2

I have a requirement to display a custom Activity which contains a listview showing all the timeZones as per below format:

ZoneID   (UTC <+/-> hh:mm)

These timezones should be sorted (similar to TimeZone settings shown in Windows) such that:

  1. All UTC negative timezones appear first
  2. All UTC+00:00 timezones appear next
  3. All timezones greater than UTC+00:00 appear last

I came across a lot of examples but they all are using JDK 1.8 (Android API level 26) with ZoneId class. Few such examples are explained here and here.

I want some alternative to these examples, which I can use in Android API level 23 or JDK 1.7.

Expected Output:

                      America/Thule (UTC-03:00)
         America/Argentina/La_Rioja (UTC-03:00)
                      America/Belem (UTC-03:00)
                      America/Jujuy (UTC-03:00)
                      America/Bahia (UTC-03:00)
                  America/Goose_Bay (UTC-03:00)
         America/Argentina/San_Juan (UTC-03:00)
   America/Argentina/ComodRivadavia (UTC-03:00)
          America/Argentina/Tucuman (UTC-03:00)
                    America/Rosario (UTC-03:00)
                    SystemV/AST4ADT (UTC-03:00)
     America/Argentina/Buenos_Aires (UTC-03:00)
                   America/St_Johns (UTC-02:30)
                Canada/Newfoundland (UTC-02:30)
                   America/Miquelon (UTC-02:00)
                          Etc/GMT+2 (UTC-02:00)
                    America/Godthab (UTC-02:00)
                    America/Noronha (UTC-02:00)
                   Brazil/DeNoronha (UTC-02:00)
             Atlantic/South_Georgia (UTC-02:00)
                          Etc/GMT+1 (UTC-01:00)
                Atlantic/Cape_Verde (UTC-01:00)
                       Africa/Dakar (UTC+00:00) 
                      Africa/Bissau (UTC+00:00) 
                                WET (UTC+00:00) 
                      Etc/Greenwich (UTC+00:00) 
                    Africa/Timbuktu (UTC+00:00) 
                    Africa/Monrovia (UTC+00:00) 
                  Europe/Bratislava (UTC+01:00) 
                Arctic/Longyearbyen (UTC+01:00) 
                     Europe/Vatican (UTC+01:00) 
                      Europe/Monaco (UTC+01:00) 
                      Africa/Harare (UTC+02:00) 
                     Europe/Tallinn (UTC+02:00) 
kkodev
  • 2,557
  • 23
  • 23

4 Answers4

2

Use a list of java.util.TimeZone and sort them by the offset:

String[] ids = TimeZone.getAvailableIDs();
List<TimeZone> zones = new ArrayList<>();
for (String id : ids) {
    zones.add(TimeZone.getTimeZone(id));
}
Collections.sort(zones, new Comparator<TimeZone>() {
    // compare by the offset, reverse order
    @Override
    public int compare(TimeZone o1, TimeZone o2) {
        return o1.getRawOffset() - o2.getRawOffset();
}});

Then you print this list, using the ID and a helper method to format the offset:

public String formatOffset(int rawOffset) {
    StringBuilder sb = new StringBuilder("UTC").append(rawOffset < 0 ? "-" : "+");
    int secs = Math.abs(rawOffset) / 1000;
    int hours = secs / 3600;
    secs -= hours * 3600;
    int mins = secs / 60;
    sb.append(hours < 10 ? "0" : "").append(hours).append(":");
    sb.append(mins < 10 ? "0" : "").append(mins);
    return sb.toString();
}

for (TimeZone tz : zones) {
    System.out.println(tz.getID() + " (" + formatOffset(tz.getRawOffset()) + ")");
}

The output will be something like:

Etc/GMT+12 (UTC-12:00)
Etc/GMT+11 (UTC-11:00)
Pacific/Midway (UTC-11:00)
Pacific/Niue (UTC-11:00)
... lots of timezones

Note that I didn't include the "pretty tabs" formatting, it's up to you.

I also used getRawOffset, that returns the current offset for that zone - check the javadoc: https://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html#getRawOffset()

This method doesn't consider Daylight Saving Time changes or any other historical data (like countries that used a different offset in the past - and believe me, most countries - if not all of them - had at least one offset change during their history). To use those other offsets, you can use the getOffset method, passing a reference date/timestamp as argument - check the javadoc for details.

I also used getAvailableIDs, that returns all the timezones, but you can filter this list to have only the zones you want.


But honestly, this old API (java.util's Date, Calendar, SimpleDateFormat, TimeZone and so on) is really bad, with lots of flaws and annoying bugs, and nobody deserves to use them anymore. Specially today, that we have better API's.

In API levels that don't have the java.time classes (such as ZoneId), you can use the ThreeTen Backport, as already told in the other answer. Then you can follow the examples you found.

One main difference is that this API always requires an Instant as a reference to get the offset - because it checks all the timezone's historical data to know what's the offset used in that specific moment (in the code below, I'm using the current instant):

Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
List<ZoneId> timezones = new ArrayList<>();
for (String id : availableZoneIds) {
    timezones.add(ZoneId.of(id));
}
// current instant
final Instant now = Instant.now();
Collections.sort(timezones, new Comparator<ZoneId>() {

    // compare by offset in reverse order, use the Instant as a reference
    @Override
    public int compare(ZoneId o1, ZoneId o2) {
        return o2.getRules().getOffset(now).compareTo(o1.getRules().getOffset(now));
    }
});
for (ZoneId zone : timezones) {
    ZoneOffset offset = zone.getRules().getOffset(now);
    System.out.println(zone + " (UTC" + (offset.getTotalSeconds() == 0 ? "+00:00" : offset.toString()) + ")");
}

Also note that the ZoneOffset class has a nice toString() method that already prints the offset in the correct format (except when it's zero, which is printed as Z, but that wasn't hard to fix).

tigr
  • 21
  • 2
  • Thankyou very much. +1 for the detailed answer. But as already stated by you that TimeZone doesn't consider Daylight savings, i cannot use it in real time as it may cause error in the times displayed. – kapilAgnihotri Feb 28 '18 at 04:43
2

As a complement to the other answer, you can print the zones and offsets using a formatter:

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("VV '(UTC'xxx')'");
for (ZoneId zone : timezones) {
    System.out.println(fmt.format(now.atZone(zone)));
}

It produces the same output: "VV" prints the zone ID and "xxx" prints the offset.

trec
  • 23
  • 3
0

Just get ThreeTenABP and add it to your Android project in order to use ZoneId and the other goodies from java.time, the modern Java date and time API that came out with Java 8 and was also backported to Java 6 and 7.

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

Solution using Joda-Time:

First get all the ID's using DateTimeZone class and then sort them using DateTimeZone.getOffset(Instant) method. DateTimeZone considers daylight savings unlike TimeZone.getrawOffset() provided by java.util.

Set<String> availableIDs = DateTimeZone.getAvailableIDs();
    List<DateTimeZone> dateTimezones = new ArrayList<>();

    for (String id : availableIDs) {
        dateTimezones.add(DateTimeZone.forID(id));
    }

    final Instant now = Instant.now();
    Collections.sort(dateTimezones, new Comparator<DateTimeZone>() {

        @Override
        public int compare(DateTimeZone o1, DateTimeZone o2) {
            return o1.getOffset(now)-o2.getOffset(now);
        }
    });
    for (DateTimeZone dateTimeZone : dateTimezones) {
        int offset = dateTimeZone.getOffset(now);
        String out = String.format("%35s %s%n", dateTimeZone, " (" + formatOffset(offset) + ")");
        System.out.printf(out);
    }

formatOffset(int) is reused from the above example.

Links :

Link to Download Joda-Time