I test and deal with Joda, which I think might be a mistake. However, it is entirely possible that I am mistaken using the library, so please give me your feedback.
We used joda for production to convert the time between our database storage format (UTC) and the user's time zone preference. Employees using the system work from Arizona, which does not include daylight saving time. Among other things, this system tracks the start time of sporting events throughout the country.
Joda did a great job for us until we noticed that the day before the time change we had some wrong results. We found that iodine seemed to change to daylight saving time at midnight UTC, and not at the appropriate time for a particular time zone. In addition, this problem only occurs when the time between the DST observation state is converted to a state other than the DST, such as Arizona.
I have prepared a complete test case that illustrates this problem. As you will see, joda provides the expected result for all test cases in the USA / East β USA / Pacific. For USA / Arizona β USA / Pacific, it works all year round until the change of new and subsequently. However, on the day the time changes (November 6), the time is incorrect. There may also be a problem of changing the time of the march, although I have not tested it exhaustively yet.
Here is the result of the test provided (the entry for November 6 illustrates the error ):
=== November 1st, Expected Result (0 hour) === java: Converting 2010-11-01 09:00 from US/Arizona to US/Pacific. Result: 2010-11-01 09:00. Change (0 hour). joda: Converting 2010-11-01 09:00 from US/Arizona to US/Pacific. Result: 2010-11-01 09:00. Change (0 hour). ======================================= === November 6th, Expected Result (0 hour) === java: Converting 2010-11-06 09:00 from US/Arizona to US/Pacific. Result: 2010-11-06 09:00. Change (0 hour). joda: Converting 2010-11-06 09:00 from US/Arizona to US/Pacific. Result: 2010-11-06 08:00. Change (-1 hour). ======================================= === November 12th, Expected Result (-1 hour) === java: Converting 2010-11-12 09:00 from US/Arizona to US/Pacific. Result: 2010-11-12 08:00. Change (-1 hour). joda: Converting 2010-11-12 09:00 from US/Arizona to US/Pacific. Result: 2010-11-12 08:00. Change (-1 hour). ======================================= === March 12th, Expected Result (-1 hour) === java: Converting 2010-03-12 09:00 from US/Arizona to US/Pacific. Result: 2010-03-12 08:00. Change (-1 hour). joda: Converting 2010-03-12 09:00 from US/Arizona to US/Pacific. Result: 2010-03-12 08:00. Change (-1 hour). ======================================= === March 14th, Expected Result (0 hour) === java: Converting 2010-03-14 09:00 from US/Arizona to US/Pacific. Result: 2010-03-14 09:00. Change (0 hour). joda: Converting 2010-03-14 09:00 from US/Arizona to US/Pacific. Result: 2010-03-14 09:00. Change (0 hour). ======================================= === November 1st, Expected Result (-3 hour) === java: Converting 2010-11-01 09:00 from US/Eastern to US/Pacific. Result: 2010-11-01 06:00. Change (-3 hour). joda: Converting 2010-11-01 09:00 from US/Eastern to US/Pacific. Result: 2010-11-01 06:00. Change (-3 hour). ======================================= === November 6th, Expected Result (-3 hour) === java: Converting 2010-11-06 09:00 from US/Eastern to US/Pacific. Result: 2010-11-06 06:00. Change (-3 hour). joda: Converting 2010-11-06 09:00 from US/Eastern to US/Pacific. Result: 2010-11-06 06:00. Change (-3 hour). ======================================= === November 12th, Expected Result (-3 hour) === java: Converting 2010-11-12 09:00 from US/Eastern to US/Pacific. Result: 2010-11-12 06:00. Change (-3 hour). joda: Converting 2010-11-12 09:00 from US/Eastern to US/Pacific. Result: 2010-11-12 06:00. Change (-3 hour). ======================================= === March 12th, Expected Result (-3 hour) === java: Converting 2010-03-12 09:00 from US/Eastern to US/Pacific. Result: 2010-03-12 06:00. Change (-3 hour). joda: Converting 2010-03-12 09:00 from US/Eastern to US/Pacific. Result: 2010-03-12 06:00. Change (-3 hour). ======================================= === March 14th, Expected Result (-3 hour) === java: Converting 2010-03-14 09:00 from US/Eastern to US/Pacific. Result: 2010-03-14 06:00. Change (-3 hour). joda: Converting 2010-03-14 09:00 from US/Eastern to US/Pacific. Result: 2010-03-14 06:00. Change (-3 hour). =======================================
And here is the full test case:
package com.test.time; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; import org.joda.time.DateTimeZone; import org.junit.Before; import org.junit.Test; public class TimeTest { Calendar nov6; Calendar nov1; Calendar nov12; Calendar mar12; Calendar mar14; @Before public void doBefore() { // November 1st 2010, 9:00pm (DST is active) nov1 = Calendar.getInstance(); nov1.setTimeZone(TimeZone.getTimeZone("US/Arizona")); nov1.set(Calendar.HOUR_OF_DAY, 21); nov1.set(Calendar.MINUTE, 0); nov1.set(Calendar.SECOND, 0); nov1.set(Calendar.YEAR, 2010); nov1.set(Calendar.MONTH, 10); // November nov1.set(Calendar.DATE, 1); // November 6st 2010, 9:00pm (DST is still active until early AM // november 7th) nov6 = Calendar.getInstance(); nov6.setTimeZone(TimeZone.getTimeZone("US/Arizona")); nov6.set(Calendar.HOUR_OF_DAY, 21); nov6.set(Calendar.MINUTE, 0); nov6.set(Calendar.SECOND, 0); nov6.set(Calendar.YEAR, 2010); nov6.set(Calendar.MONTH, 10); // November nov6.set(Calendar.DATE, 6); // November 12th 2010, 9:00pm (DST has ended) nov12 = Calendar.getInstance(); nov12.setTimeZone(TimeZone.getTimeZone("US/Arizona")); nov12.set(Calendar.HOUR_OF_DAY, 21); nov12.set(Calendar.MINUTE, 0); nov12.set(Calendar.SECOND, 0); nov12.set(Calendar.YEAR, 2010); nov12.set(Calendar.MONTH, 10); // November nov12.set(Calendar.DATE, 12); // March 12th 2011, 9:00pm (DST has ended, will begin early am march // 13th) mar12 = Calendar.getInstance(); mar12.setTimeZone(TimeZone.getTimeZone("US/Arizona")); mar12.set(Calendar.HOUR_OF_DAY, 21); mar12.set(Calendar.MINUTE, 0); mar12.set(Calendar.SECOND, 0); mar12.set(Calendar.YEAR, 2010); mar12.set(Calendar.MONTH, 2); // March mar12.set(Calendar.DATE, 12); // March 14th 2011, 9:00pm (DST has started) mar14 = Calendar.getInstance(); mar14.setTimeZone(TimeZone.getTimeZone("US/Arizona")); mar14.set(Calendar.HOUR_OF_DAY, 21); mar14.set(Calendar.MINUTE, 0); mar14.set(Calendar.SECOND, 0); mar14.set(Calendar.YEAR, 2010); mar14.set(Calendar.MONTH, 2); // March mar14.set(Calendar.DATE, 14); } @Test public void testArizonaToPacific() { System.out.println("=== November 1st, Expected Result (0 hour) ==="); timeTestJava(nov1.getTime(), "US/Arizona", "US/Pacific"); timeTestJoda(nov1.getTime(), "US/Arizona", "US/Pacific"); System.out.println("=======================================\n"); System.out.println("=== November 6th, Expected Result (0 hour) ==="); timeTestJava(nov6.getTime(), "US/Arizona", "US/Pacific"); timeTestJoda(nov6.getTime(), "US/Arizona", "US/Pacific"); System.out.println("=======================================\n"); System.out.println("=== November 12th, Expected Result (-1 hour) ==="); timeTestJava(nov12.getTime(), "US/Arizona", "US/Pacific"); timeTestJoda(nov12.getTime(), "US/Arizona", "US/Pacific"); System.out.println("=======================================\n"); System.out.println("=== March 12th, Expected Result (-1 hour) ==="); timeTestJava(mar12.getTime(), "US/Arizona", "US/Pacific"); timeTestJoda(mar12.getTime(), "US/Arizona", "US/Pacific"); System.out.println("=======================================\n"); System.out.println("=== March 14th, Expected Result (0 hour) ==="); timeTestJava(mar14.getTime(), "US/Arizona", "US/Pacific"); timeTestJoda(mar14.getTime(), "US/Arizona", "US/Pacific"); System.out.println("=======================================\n"); } @Test public void testEasternToPacific() { System.out.println("=== November 1st, Expected Result (-3 hour) ==="); timeTestJava(nov1.getTime(), "US/Eastern", "US/Pacific"); timeTestJoda(nov1.getTime(), "US/Eastern", "US/Pacific"); System.out.println("=======================================\n"); System.out.println("=== November 6th, Expected Result (-3 hour) ==="); timeTestJava(nov6.getTime(), "US/Eastern", "US/Pacific"); timeTestJoda(nov6.getTime(), "US/Eastern", "US/Pacific"); System.out.println("=======================================\n"); System.out.println("=== November 12th, Expected Result (-3 hour) ==="); timeTestJava(nov12.getTime(), "US/Eastern", "US/Pacific"); timeTestJoda(nov12.getTime(), "US/Eastern", "US/Pacific"); System.out.println("=======================================\n"); System.out.println("=== March 12th, Expected Result (-3 hour) ==="); timeTestJava(mar12.getTime(), "US/Eastern", "US/Pacific"); timeTestJoda(mar12.getTime(), "US/Eastern", "US/Pacific"); System.out.println("=======================================\n"); System.out.println("=== March 14th, Expected Result (-3 hour) ==="); timeTestJava(mar14.getTime(), "US/Eastern", "US/Pacific"); timeTestJoda(mar14.getTime(), "US/Eastern", "US/Pacific"); System.out.println("=======================================\n"); } // print some output from the test private void print(Date startTime, String text, String from, String to, Date output) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm"); System.out.println(text + ":"); System.out.println("Converting " + sdf.format(startTime) + " from " + from + " to " + to + "."); long difference = output.getTime() - startTime.getTime(); System.out.println("Result: " + sdf.format(output) + ". Change (" + difference / 1000 / 60 / 60 + " hour).\n"); } // wrapper around joda test private void timeTestJoda(Date startTime, String from, String to) { Date output = convertJodaOld(startTime, TimeZone.getTimeZone(from), TimeZone.getTimeZone(to)); print(startTime, "joda", from, to, output); } // wrapper around java test private void timeTestJava(Date startTime, String from, String to) { Date output = convertJava(startTime, TimeZone.getTimeZone(from), TimeZone.getTimeZone(to)); print(startTime, "java", from, to, output); } // Joda implementation, works before and after DST change, but not during // the period from 2am-7am UTC on the day of the change public Date convertJodaOld(Date dt, TimeZone from, TimeZone to) { DateTimeZone tzFrom = DateTimeZone.forTimeZone(from); DateTimeZone tzTo = DateTimeZone.forTimeZone(to); Date utc = new Date(tzFrom.convertLocalToUTC(dt.getTime(), false)); Date convertedTime = new Date(tzTo.convertUTCToLocal(utc.getTime())); return convertedTime; } // Java implementation. Works. public Date convertJava(Date dt, TimeZone from, TimeZone to) { long fromOffset = from.getOffset(dt.getTime()); long toOffset = to.getOffset(dt.getTime()); long convertedTime = dt.getTime() - (fromOffset - toOffset); return new Date(convertedTime); } }
Thanks!