Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compare TimeZones

I need to compare time zones such that Asia/Singapore < UTC < Pacific/Honolulu.

I'm working with java.util.TimeZone (which doesn't implement Comparable).

My search for an existing implementation was unsuccessful because of the overwhelming number of questions about comparing dates with different time zones.

Question: What is a correct implementation of Comparator<TimeZone> that will solve this problem (and what makes it better than other solutions, if applicable)?

Note that I'm not able to use Joda Time for this problem, so "use Joda Time" is not a valid answer.

Edit for clarity

The < notation above was not well defined. My particular use case only requires a naive "geographical" ordering from east to west. As the comments have pointed out, a more advanced and generalizable solution would take into account temporal factors like daylight savings time and historical GMT offset changes. So I think there are two orderings we can consider, each requiring a different Comparator<TimeZone> implementation:

  • Strictly geographical (current UTC) - addressed by my answer.
  • Sensitive to local or civil time changes - addressed by rgettman's answer.
like image 900
Paul Bellora Avatar asked Mar 02 '13 00:03

Paul Bellora


2 Answers

I rolled my own Comparator<TimeZone> implementation using getRawOffset for the comparison:

@Override
public int compare(TimeZone tz1, TimeZone tz2) {
    return tz2.getRawOffset() - tz1.getRawOffset();
}

It seems to have passed a quick test:

final List<TimeZone> timeZones = Arrays.asList(
        TimeZone.getTimeZone("UTC"),
        TimeZone.getTimeZone("America/Los_Angeles"),
        TimeZone.getTimeZone("America/New_York"),
        TimeZone.getTimeZone("Pacific/Honolulu"),
        TimeZone.getTimeZone("Asia/Singapore")
);

final List<TimeZone> expectedOrder = Arrays.asList(
        TimeZone.getTimeZone("Asia/Singapore"),
        TimeZone.getTimeZone("UTC"),
        TimeZone.getTimeZone("America/New_York"),
        TimeZone.getTimeZone("America/Los_Angeles"),
        TimeZone.getTimeZone("Pacific/Honolulu")
);

Collections.sort(timeZones, new Comparator<TimeZone>() {
    @Override
    public int compare(TimeZone tz1, TimeZone tz2) {
        return tz2.getRawOffset() - tz1.getRawOffset();
    }
});

//Impl note: see AbstractList.equals
System.out.println(timeZones.equals(expectedOrder)); //true

But I'm still wondering whether there are pitfalls to this solution and/or if there's something preferable.

like image 65
Paul Bellora Avatar answered Sep 27 '22 23:09

Paul Bellora


One might be able to create a Comparator<TimeZone> that takes into account time zone differences. The TimeZone may or may not obvserve daylight savings time, which would adjust the raw offset, thus messing up raw-offset-only comparisons. The TimeZone class seems to support the adjustment based on the 2 getOffset methods, but they need a reference date. How about:

public class TimeZoneComparator implements Comparator<TimeZone>
{
   private long date;

   public TimeZoneComparator(long date)
   {
      this.date = date;
   }

   public int compare(TimeZone tz1, TimeZone tz2)
   {
      return tz2.getOffset(this.date) - tz2.getOffset(this.date);
   }
}
like image 43
rgettman Avatar answered Sep 27 '22 23:09

rgettman