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.