5

I need to convert GMT to Arizona time. Arizona currently has MST (Daylight Saving off).

However in Joda Time DateTimeZone code, MST has been mapped to America/Denver:

map.put("MST", "America/Denver");

It can be seen that currently Denver has Daylight Saving on and hence has MDT. Then why has such a mapping been made in DateTimeZone code?

While converting to local time zone for arizona from GMT, I am getting offset as GMT-6 which is wrong and should have been GMT-7 since MST does not have Daylight Saving.

Is this a bug? How to solve the issue?

user3804428
  • 139
  • 1
  • 7
  • 2
    Could you just specify `America/Phoenix` explicitly? (I don’t know JodaTime, but I am guessing it should be able to understand.) – Ole V.V. Mar 26 '17 at 12:21
  • 2
    If all else fails, you may consider switcing over to Java 8 `java.time` date and time classes or their backport to Java 6 and 7, ThreeTen Backport. If you already have a large codebase using JodaTime, this is not what you want, of course. – Ole V.V. Mar 26 '17 at 12:24

2 Answers2

1

At first I thought that Joda time would use the same TimeZone IDs as base Java's TimeZone. I debugged through the code and found that it was a little more complicated. While there may be a parallel to Joda using the same IDs as base Java, it's actually loading the information for the time zones from files in the joda jar. This example is done with all Joda classes. dt came out as having the Phoenix, Arizona time.

public static void main(String[] args)
{
  //List all time zones
  Set<String> timezones = DateTimeZone.getAvailableIDs();
  for(String tz : timezones)
  {
    System.out.println(tz);
  }

  DateTimeZone arizona = DateTimeZone.forID("US/Arizona");
  DateTimeZone.setDefault(arizona);
  DateTime dt = new DateTime();
  System.out.println(dt);

}
ProgrammersBlock
  • 5,974
  • 4
  • 17
  • 21
1

The problem with these 3-letter abbreviations is that they are ambiguous and not standard.

So, MST is a timezone name that can be applied to lots of different regions, and they might have different DST rules (some might have DST, others might not, and not to mention the historical information - some might had DST in the past and currently don't have anymore, or the opposite).

The old API (java.util.TimeZone) handled these ambiguities assuming default values for those ambiguous 3-letter names, so MST was mapped to America/Denver and this was kept in joda-time - IMO - for compatibility reasons (you can see in the source code - I'm using jodatime 2.7). At least that's what I could understand from the comment:

map.put("MST", "America/Denver");  // JDK 1.1 compatible

And this map is only used in DateTimeZone.forTimeZone(TimeZone), a method to convert the old TimeZone class to jodatime's DateTimeZone (which, for me, confirms the compatibility reason).


If you want to make sure your code uses the correct timezone, you could do as already told in @Ole V.V.'s comment and use America/Phoenix instead of MST. This will remove the ambiguity and make your code more clear about what timezone it's using.

These kind of timezone names (Continent/City) comes from IANA database and that's the names used by Java's API and Joda time.

Example code:

// create a date/time in UTC
DateTime utc = new DateTime("2017-03-26T10:00:00Z", DateTimeZone.UTC);
System.out.println(utc); // 2017-03-26T10:00:00.000Z

// convert to Arizona timezone
DateTimeZone arizona = DateTimeZone.forID("America/Phoenix");
DateTime arizonaDate = utc.withZone(arizona);
System.out.println(arizonaDate); // 2017-03-26T03:00:00.000-07:00

New Java Time API

If possible, I suggest you to use the new Date/Time API (if migrating from Joda-time to this new API is not possible, desconsider the rest of this answer; but if you want to migrate it, just go on).

The problem with joda is because in joda's website it says: Note that Joda-Time is considered to be a largely "finished" project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).

If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.

If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).

The code below works for both. The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.

In the new API, it's possible to map MST to the timezone you want, using a custom Map:

// custom map with timezone names
Map<String, String> map = new HashMap<>();
// map "MST" to Arizona's timezone
map.put("MST", "America/Phoenix");
// create timezone
ZoneId arizona = ZoneId.of("MST", map);

// datetime in UTC
ZonedDateTime utc = ZonedDateTime.parse("2017-03-26T10:00:00Z");
System.out.println(utc); // 2017-03-26T10:00Z

// convert to Arizona timezone
ZonedDateTime arizonaDate = utc.withZoneSameInstant(arizona);
System.out.println(arizonaDate); // 2017-03-26T03:00-07:00[America/Phoenix]

Of course the ideal is to use ZoneId.of("America/Phoenix"), but if it's not possible and you must use MST, the custom map is a good alternative.

Also note that the new API prints the timezone name between []. If you want to change it, you can use the DateTimeFormatter class. You can either use a built-in formatter (check javadoc for more info), or make your own:

// use built-in formatter
DateTimeFormatter fmt = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
System.out.println(fmt.format(arizonaDate)); // 2017-03-26T03:00:00-07:00

// use custom format
fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
System.out.println(fmt.format(arizonaDate)); // 2017-03-26T03:00:00.000-07:00