We have an application in which time is critical. We use joda to convert time and store all data in UTC. We were in production for a while, and everything was perfect, BUT ...
Now we notice that the events that occurred several hours before the time change were already converted too soon! In fact, the UTC time stored in the database is disabled for an hour.
Here is an example. My event takes place on 11/6/2010 @ 9pm PDT and will usually be saved as 11/7/2010 @ 4am. However, since Daylight Savings Time ended on the 7th (presumably at 2 a.m.), this time it was changed and saved as 11/7/2010 @ 5 a.m.
We need to ensure that the DST change is not recorded until it actually occurs in the PST region, at 2 a.m. PST. I suggested that joda handle this, especially since it advertises significantly improved over standard Java features.
Any feedback you get will be helpful, especially if you can get it before tomorrow's change! After that, it will be an academic, but still useful discussion.
Here are some of the code we use to perform the time zone change, and get the result as a regular java date object.
public Date convertToTimeZone(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;
}
Edit: sample code for comments below
public Date convert(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);
}
Complete Unit Test Example
package com.test.time;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Instant;
import org.junit.Before;
import org.junit.Test;
public class TimeTest {
Calendar nov6;
Calendar nov1;
Calendar nov12;
@Before
public void doBefore() {
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);
nov1.set(Calendar.DATE, 1);
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);
nov6.set(Calendar.DATE, 6);
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);
nov12.set(Calendar.DATE, 12);
}
@Test
public void test1() {
timeTestJava(nov1.getTime(), "equivalent", "US/Arizona", "US/Pacific");
timeTestJodaOld(nov1.getTime(), "equivalent", "US/Arizona", "US/Pacific");
timeTestJodaNew(nov1.getTime(), "equivalent", "US/Arizona", "US/Pacific");
timeTestJava(nov6.getTime(), "equivalent", "US/Arizona", "US/Pacific");
timeTestJodaOld(nov6.getTime(), "equivalent", "US/Arizona", "US/Pacific");
timeTestJodaNew(nov6.getTime(), "equivalent", "US/Arizona", "US/Pacific");
timeTestJava(nov12.getTime(), "minus1", "US/Arizona", "US/Pacific");
timeTestJodaOld(nov12.getTime(), "minus1", "US/Arizona", "US/Pacific");
timeTestJodaNew(nov12.getTime(), "minus1", "US/Arizona", "US/Pacific");
}
private void timeTestJodaOld(Date startTime, String text, String from, String to) {
System.out.println("joda_old: " + startTime);
Date output = convertJodaOld(startTime, TimeZone.getTimeZone(from), TimeZone.getTimeZone(to));
System.out.println(text + ": " + output + "\n");
}
private void timeTestJodaNew(Date startTime, String text, String from, String to) {
System.out.println("joda_new: " + startTime);
Date output = convertJodaNew(startTime, TimeZone.getTimeZone(from), TimeZone.getTimeZone(to));
System.out.println(text + ": " + output + "\n");
}
private void timeTestJava(Date startTime, String text, String from, String to) {
System.out.println("java: " + startTime);
Date output = convertJava(startTime, TimeZone.getTimeZone(from), TimeZone.getTimeZone(to));
System.out.println(text + ": " + output + "\n");
}
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;
}
public Date convertJodaNew(Date dt, TimeZone from, TimeZone to) {
Instant utcInstant = new Instant(dt.getTime());
DateTime datetime = new DateTime(utcInstant);
datetime.withZone(DateTimeZone.forID(to.getID()));
return datetime.toDate();
}
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);
}
}