ThreeTen: The Ultimate Future of Java Date/Time…In Progress

by on November 22, 2011 8:06 am

My last article was entitled Joda-Time: The Future of Java Date/Time and I was pleased to see a lot of discussion on the topic. However, I may well have inappropriately named the article. I suppose I should have called it “Joda-Time: A Glimpse of the Future of Java Date/Time” or “Joda-Time: A Step on the Way to JSR-310.” Thus the title of this article, and the inspiration for this follow-up.

The main reason I had focused on Joda-Time instead of JSR-310 (colloquially known as ThreeTen) is because Joda-Time is a mature framework that has many examples and published tutorials, examples, etc. Additionally, many people are using Joda-Time.  Also, the basic concepts in Joda-Time will translate (at least partially) into ThreeTen, so it’s a worthwhile investment of time.

I won’t discuss all the changes and improvements in ThreeTen; that’s covered well on the ThreeTen Project website.

One of the things you will notice upon reviewing the website is that ThreeTen still is in Alpha. Examples are not as plentiful, and the methods and classes still are in flux. In fact, the author of Joda-Time and the lead on ThreeTen recommends against its use in his comment on this Stack Overflow discussion. Additionally, on this very long discussion thread at The Server Side, Colebourne notes that the JDBC date classes are under the control of the JDBC group and are not part of ThreeTen. Also, he notes that there is a greater need for data conversion methods between date/time objects.

So armed with that research, I thought it might be good to revisit my previous article’s examples, noting how they would change with ThreeTen, and get an idea of the readiness of the ThreeTen project. So I headed on over to the ThreeTen Project and downloaded version 0.6.3– which I was surprised to see was nearly a year old!

Reviewing the code, I noted that the classes were similarly named to the Joda-Time classes but existed in the javax.time package. I also noticed that three Java classes: java.util.Calendar, java.util.GregorianCalendar, and java.util.Date all had been changed for tighter integration.

In my previous article on Joda-Time, I showed how complex it was to use the current Java date/time classes to create a date object representing the Apollo 11 moon landing, which occurred on July 20, 1969 at 4:17 pm US/Eastern time. Here’s how it was done in Joda-Time, first creating the date object in Eastern US time and then converting it to Central US time:

DateTimeFormatter fmtDateTime = DateTimeFormat.forPattern("M/dd/yyyy h:mm a");
fmtDateTime = fmtDateTime.withZone(DateTimeZone.forID("US/Eastern"));
DateTime dateTimeWithZone = new DateTime(fmtDateTime.parseDateTime("7/20/1969 4:17 pm"));
System.out.println("DateTimeString = " + dateTimeString + " --> JodaDateTime FORMATTED W TZ = " + dateTimeWithZone.toString("MMMM dd, yyyy h:m z"));
DateTime dateTimeCentral = new DateTime(dateTimeWithZone, DateTimeZone.forID("US/Central"));
System.out.println("DateTimeString = 7/20/1969 4:17 PM --> JodaDateTime FORMATTED W Central TZ = " + dateTimeCentral.toString("MMMM dd, yyyy h:m z));

Now you can see the output:

DateTimeString = 7/20/1969 4:17 pm --> JodaDateTime FORMATTED W TZ = July 20, 1969 4:17 EDT
DateTimeString = 7/20/1969 4:17 pm -->; JodaDateTime FORMATTED W Central TZ = July 20, 1969 3:17 CDT

Now let’s look at how to create the date object representing the Eastern US time zone using ThreeTen:

DateTimeFormatter dtzf = new DateTimeFormatterBuilder().appendPattern("M/dd/yyyy h:mm a z").toFormatter();
ZonedDateTime dateTimeWithZone = ZonedDateTime.parse("7/20/1969 4:17 pm EST", dtzf);

Whoa. Can’t parse the date string:

Text '7/20/1969 4:17 pm EDT' could not be parsed at index 15

Okay, definitely either I have the formatting string wrong or there’s a problem with the formatter. Let’s try this again using the standard ISO-8601 date format:

ateTimeFormatter dtzf = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd'T'HH:mm:ss.SSS Z").toFormatter();
ZonedDateTime dateTimeWithZone = ZonedDateTime.parse("1969-07-20T16:17:00.000 -0500", dtzf);
System.out.println("ZonedDateTime String = 7/20/1969 4:17 PM --> 310ZonedDateTime FORMATTED W TZ = " + dateTimeWithZone.toString(dtzf));

It seems that the code above also throws an exception… it is able to parse the string into an object, but isn’t able to create the object we are asking for.

Text '1969-07-20T16:17:00.000 -0500' could not be parsed into ZonedDateTime but was parsed to {ISO.OffsetDateTime=1969-07-20T16:17-05:00}

I dug into the code and saw that in the CalendricalMerger class, the ISOChronology singleton is used to merge the date created into the date we want; however, there is a note on that item: “TODO better?”. Debugging through the source, I found that the merge() method dropped all the way to the end without properly merging the object.

In fact, I ran into several issues parsing dates into the appropriate object. This definitely is an area that needs work in ThreeTen, especially if you are using it to parse user input. Definitely some work to be done here, as noted by the developers, since I expected standard formats to provide similar results to Joda-Time.

So to continue with this example, I used the following code:

 ZonedDateTime dateTimeWithZone = ZonedDateTime.of(1969, MonthOfYear.JULY, 20, 16, 17, 0, 0,    javax.time.calendar.TimeZone.of("US/Eastern"));
DateTimeFormatter dtzf = new DateTimeFormatterBuilder().appendPattern("M/dd/yyyy h:mm a z").toFormatter();
System.out.println("ZonedDateTime String = 7/20/1969 4:17 PM --> 310ZonedDateTime FORMATTED W TZ = " + dateTimeWithZone.toString(dtzf));
ZonedDateTime centralDateTimeZone = dateTimeWithZone.toOffsetDateTime().atZoneSameInstant(javax.time.calendar.TimeZone.of("US/Central"));
System.out.println("ZonedDateTime EASTERN String = 7/20/1969 4:17 PM --> 310ZonedDateTime CENTRAL FORMATTED W TZ = " + centralDateTimeZone.toString(dtzf));

This gave us some odd results:

ZonedDateTime String = 7/20/1969 4:17 PM --> 310ZonedDateTime FORMATTED W TZ = 7/20/1969 3:17 PM America/Indianapolis
ZonedDateTime EASTERN String = 7/20/1969 4:17 PM --> 310ZonedDateTime CENTRAL FORMATTED W TZ = 7/20/1969 2:17 PM America/Chicago

It looks to me that the formatter adjusts the time to my current time zone (US/Central) instead of the expected US/Eastern time zone. So I tried a different formatter:

ZonedDateTime dateTimeWithZone = ZonedDateTime.of(1969, MonthOfYear.JULY, 20, 16, 17, 0, 0, javax.time.calendar.TimeZone.of("US/Eastern"));
DateTimeFormatter dtzf = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd'T'HH:mm:ss.SSS Z").toFormatter();
System.out.println("ZonedDateTime String = 7/20/1969 4:17 PM --> 310ZonedDateTime FORMATTED W TZ = " + dateTimeWithZone.toString(dtzf));
ZonedDateTime centralDateTimeZone = dateTimeWithZone.toOffsetDateTime().atZoneSameInstant(javax.time.calendar.TimeZone.of("US/Central"));
System.out.println("ZonedDateTime EASTERN String = 7/20/1969 4:17 PM --> 310ZonedDateTime CENTRAL FORMATTED W TZ = " + centralDateTimeZone.toString(dtzf));

And now the times appear as expected:

ZonedDateTime String = 7/20/1969 4:17 PM --> 310ZonedDateTime FORMATTED W TZ = 1969-07-20T16:17:00.000 US/Eastern
ZonedDateTime EASTERN String = 7/20/1969 4:17 PM --> 310ZonedDateTime CENTRAL FORMATTED W TZ = 1969-07-20T15:17:00.000 US/Central

This looks like another issue with the formatter. While I like the approach and usability of the newer ThreeTen objects, this concerns me and seems to be a good reason why this library, while promising, still is not production ready.

My last article indicated an example of Joda-Time working with Daylight Savings Time. ThreeTen works exactly the same in this regard (with the exception of parsing a String to a date), using ZonedDateTime as the replacement for the DateTime object and a slightly different exception:

ZonedDateTime dateSpringForward = null;
try {
    dateSpringForward = ZonedDateTime.of(2011, MonthOfYear.MARCH, 13, 2, 5, 0, 0,
    javax.time.calendar.TimeZone.of("America/Chicago"));
    System.out.println("Spring forward date = " + dateSpringForward);
    } catch (CalendricalException e) {
    System.err.println("Hey! Time doesn't exist! --> " + e.getMessage());
}

Which produces the following output, as expected:

Hey! Time doesn't exist! -->; Local time 2011-03-13T02:05 does not exist in time-zone America/Chicago due to a gap in the local time-line

One thing that I loved having in Joda-Time was the DateMidnight object…but that currently does not exist in ThreeTen.

In the future I’d like to evaluate and compare the date manipulation features of ThreeTen and see how they compare and improve on Joda-Time. However, at this time, based on the examples provided, it seems that ThreeTen has some important improvements– and some significant flaws. Thus, while this framework holds promise for the future, it definitely is not ready for use at this time.

Not that Stephen Colebourne didn’t already tell you that, though.

– David Kieras, asktheteam@keyholesoftware.com

  • Share:

2 Responses to “ThreeTen: The Ultimate Future of Java Date/Time…In Progress”

  1. I think the 310 offers some interesting changes and the idea of tying a TimeZone to a Date object is in fact a good idea for some things (such as the Apollo landing which should justifiably be EDT). However, I am still baffled at the issues people make about the use of Dates in Java. Here’s my counter code to your original article:

    public static void main(String[] args)
    {
    TimeZone local = TimeZone.getDefault();
    TimeZone eastern = TimeZone.getTimeZone(“US/Eastern”);
    TimeZone utc = TimeZone.getTimeZone(“UTC”);
    SimpleDateFormat sdf = new SimpleDateFormat(“MMMMM dd yyyy hh:mm a Z z”);
    /* July 20, 1969 at 4:17 pm US/Eastern */
    Calendar apollo = Calendar.getInstance(eastern);
    apollo.set(1969, Calendar.JULY, 20, 16, 17);

    sdf.setTimeZone(utc);
    System.out.println(sdf.format(apollo.getTime()));
    sdf.setTimeZone(eastern);
    System.out.println(sdf.format(apollo.getTime()));
    sdf.setTimeZone(local);
    System.out.println(sdf.format(apollo.getTime()));
    }

    //OUTPUT
    July 20 1969 08:17 PM +0000 UTC
    July 20 1969 04:17 PM -0400 EDT
    July 20 1969 04:17 PM -0400 EDT //America/New York EST (It’s November)

    What’s interesting about 310 is the possibility of tying a TimeZone to a Date. That would be very useful in a number of situations. However, the real problem with date issues in Java can be solved with the simple use of RTFM.
    Calendar.setTimeZone() <– this alters input, not the output. Thus, set the time zone of the format, not the calendar and your original code works as expected.

    These are good articles and they draw a lot of focus to a serious area of confusion around dates. The API is not as clear as it could be and JSR 310 may help address that. JodaTime is another attempt (I don't see an advantage, but it is cleaner in some aspects and clean code is important).

    In short, perhaps the future of DateTime needs to be:

    public class ZoneDateTime
    {
    private TimeZone zone;
    private Date date;
    }

    Its nice to see a formal specification that would allow "global" times.

Leave a Reply

Things Twitter is Talking About