I just announced the new Spring 5 modules in REST With Spring:

>> CHECK OUT THE COURSE

1. Overview

Whenever we deal with times and dates, we need a frame of reference. The standard for that is UTC, but we also see GMT in some applications.

In short, UTC is the standard, while GMT is a time zone.

This is what Wikipedia tells us regarding what to use:

For most purposes, UTC is considered interchangeable with Greenwich Mean Time (GMT), but GMT is no longer precisely defined by the scientific community.

In other words, once we compile a list with time zone offsets in UTC, we’ll have it for GMT as well.

First, we’ll have a look at the Java 8 way of achieving this and then we’ll see how we can get the same result in Java 7.

2. Getting a List Of Zones

To start with, we need to retrieve a list of all defined time zones.

For this purpose, the ZoneId class has a handy static method:

Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();

Then, we can use the Set to generate a sorted list of time zones with their corresponding offsets:

public List<String> getTimeZoneList(OffsetBase base) {
 
    LocalDateTime now = LocalDateTime.now();
    return ZoneId.getAvailableZoneIds().stream()
      .map(ZoneId::of)
      .sorted(new ZoneComparator())
      .map(id -> String.format(
        "(%s%s) %s", 
        base, getOffset(now, id), id.getId()))
      .collect(Collectors.toList());
}

The method above uses an enum parameter which represents the offset we want to see:

public enum OffsetBase {
    GMT, UTC
}

Now let’s go over the code in more detail.

Once we’ve retrieved all available zone IDs, we need an actual time reference, represented by LocalDateTime.now().

After that, we use Java’s Stream API to iterate over each entry in our set of time zone String id’s and transform it into a list of formatted time zones with the corresponding offset.

For each of these entries, we generate a ZoneId instance with map(ZoneId::of).

3. Getting Offsets

We also need to find actual UTC offsets. For example, in the case of Central European Time, the offset would be +01:00.

To get the UTC offset for any given zone, we can use LocalDateTime’s getOffset() method.

Also note that Java represents +00:00 offsets as Z.

So, to have a consistent looking String for time zones with the zero offset, we’ll replace Z with +00:00:

private String getOffset(LocalDateTime dateTime, ZoneId id) {
    return dateTime
      .atZone(id)
      .getOffset()
      .getId()
      .replace("Z", "+00:00");
}

4. Making Zones Comparable

Optionally, we can also sort the time zones according to offset.

For this, we’ll use a ZoneComparator class:

private class ZoneComparator implements Comparator<ZoneId> {

    @Override
    public int compare(ZoneId zoneId1, ZoneId zoneId2) {
        LocalDateTime now = LocalDateTime.now();
        ZoneOffset offset1 = now.atZone(zoneId1).getOffset();
        ZoneOffset offset2 = now.atZone(zoneId2).getOffset();

        return offset1.compareTo(offset2);
    }
}

5. Displaying Time Zones

All that’s left to do is putting the above pieces together by calling the getTimeZoneList() method for each OffsetBase enum value and displaying the lists:

public class TimezoneDisplayApp {

    public static void main(String... args) {
        TimezoneDisplay display = new TimezoneDisplay();

        System.out.println("Time zones in UTC:");
        List<String> utc = display.getTimeZoneList(
          TimezoneDisplay.OffsetBase.UTC);
        utc.forEach(System.out::println);

        System.out.println("Time zones in GMT:");
        List<String> gmt = display.getTimeZoneList(
          TimezoneDisplay.OffsetBase.GMT);
        gmt.forEach(System.out::println);
    }
}

When we run the above code, it’ll print the time zones for UTC and GMT.

Here’s a snippet of how the output will look like:

Time zones in UTC:
(UTC+14:00) Pacific/Apia
(UTC+14:00) Pacific/Kiritimati
(UTC+14:00) Pacific/Tongatapu
(UTC+14:00) Etc/GMT-14

6. Java 7 and Before

Java 8 makes this task easier by using the Stream and Date and Time APIs.

However, if we have a Java 7 and before a project, we can still achieve the same result by relying on the java.util.TimeZone class with its getAvailableIDs() method:

public List<String> getTimeZoneList(OffsetBase base) {
    String[] availableZoneIds = TimeZone.getAvailableIDs();
    List<String> result = new ArrayList<>(availableZoneIds.length);

    for (String zoneId : availableZoneIds) {
        TimeZone curTimeZone = TimeZone.getTimeZone(zoneId);
        String offset = calculateOffset(curTimeZone.getRawOffset());
        result.add(String.format("(%s%s) %s", base, offset, zoneId));
    }
    Collections.sort(result);
    return result;
}

The main difference with the Java 8 code is the offset calculation.

The rawOffset we get from TimeZone()‘s getRawOffset() method expresses the time zone’s offset in milliseconds.

Therefore, we need to convert this to hours and minutes using the TimeUnit class:

private String calculateOffset(int rawOffset) {
    if (rawOffset == 0) {
        return "+00:00";
    }
    long hours = TimeUnit.MILLISECONDS.toHours(rawOffset);
    long minutes = TimeUnit.MILLISECONDS.toMinutes(rawOffset);
    minutes = Math.abs(minutes - TimeUnit.HOURS.toMinutes(hours));

    return String.format("%+03d:%02d", hours, Math.abs(minutes));
}

7. Conclusion

In this quick tutorial, we’ve seen how we can compile a list of all available time zones with their UTC and GMT offsets.

And, as always, the full source code for the examples is available over on GitHub, both the Java 8 version and Java 7 version.

I just announced the new Spring 5 modules in REST With Spring:

>> CHECK OUT THE LESSONS