Blog

DateTime and TimeZone pains

31 Mar, 2008
Xebia Background Header Wave

For as long as Java has been around, java.util.Date and java.util.Calendar have been nuisances. This will hopefully very soon be a thing of the past with the addition of JSR-310, the Date and Time API, to the Java API. At the basis of JSR-310 lies the Joda time library, which has been around for quite some time as a replacement for the standard Date and Calendar classes. However that this API is not without its own peculiarities need not come as a surprise, given the complexity of the human interpretation of time all over the world.

In our current project we have a value in the database that contains a timestamp in the UTC or "Zulu" timezone. This timestamp is fetched from the database using a java.sql.Timestamp instance. Our application however deals with org.joda.time.DateTime instances in the local time zone, which for the Netherlands are either CET (UTC+0100) or CEST (UTC+0200) depending on Daylight Savings Time.
So in a nutshell our problem encompasses converting a UTC java.sql.Timestamp to an org.joda.time.DateTime in the correct time zone, which represents the correct time. Let’s start off with writing a small skeleton JUnit test:

public class DateTimePainsTest extends TestCase {
    private static final String WRONG_TIME = "2008-03-26T20:13:39.059+01:00";
    private static final String CORRECT_TIME = "2008-03-26T21:13:39.059+01:00";
    private long millis;
    private Timestamp timestamp;
    protected void setUp() throws Exception {
        millis = new Date(1206558819059L).getTime(); // 2008-03-26T20:13:39.059+00:00
        timestamp = new Timestamp(millis);
    }
    public void testMillisAndTimestamp() {
        assertEquals(millis, timestamp.getTime());
        assertEquals("2008-03-26 20:13:39.059", timestamp.toString());
    }
}

Now we want to convert this timestamp to a DateTime object representing the following string: 2008-03-26T21:13:39.059+01:00. We first try the most simple option we can think of:

public void testDateTimeConversion1() {
        DateTime dateTime = new DateTime(timestamp.getTime(),
                DateTimeZone.forOffsetHours(1));
        assertEquals(CORRECT_TIME, dateTime.toString());
}

This test fails. In order to make the test succeed, we have to expect WRONG_TIME in there. Though according to the JavaDoc of DateTime this should have been a step in the right direction:
DateTime(long instant, DateTimeZone zone)
Constructs an instance set to the milliseconds from 1970-01-01T00:00:00Z using ISOChronology in the specified time zone.

Maybe we should first force the time zone to UTC before converting the DateTime to a European time zone. Let’s give that a try:

public void testDateTimeConversion2() {
        DateTime dateTime = new DateTime(timestamp.getTime(), DateTimeZone.UTC)
                .withZone(DateTimeZone.forOffsetHours(1));
        assertEquals(CORRECT_TIME, dateTime.toString());
}

This test also fails, substituting WRONG_TIME for CORRECT_TIME yields a green bar, but an undesired result, so we should take a different approach. LocalDateTime is a Joda time class which does not take into account any time zone information. May we can convert this to a DateTime in the correct time zone. Let’s write a new test case:

public void testDateTimeConversion3() {
        DateTime dateTime = new LocalDateTime(timestamp)
                .toDateTime(DateTimeZone.forOffsetHours(1));
        assertEquals(CORRECT_TIME, dateTime.toString());
}

Again, this test fails. We need to expect the WRONG_TIME in there to get a green bar. What seems to do the trick is converting the LocalDateTime to a DateTime in the UTC time zone, and then assigning the correct time zone to that. Lo and behold, the following test case works:

public void testDateTimeConversion4() {
        DateTime try3 = new LocalDateTime(timestamp).toDateTime(DateTimeZone.UTC)
                .withZone(DateTimeZone.forOffsetHours(1));
        assertEquals(CORRECT_TIME, try3.toString());
}

Though this solution works, I think it is not the most optimal solution. Does anyone know of a better solution? If so, please add a comment.

Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts