Date Handling in Java Revisited

Posted on February 23, 2005 by Scott Leberknight

After writing last week about how horrendous date handling in Java can be, apparently the developers who just released version 1.0 of Joda Time agreed. But they actually did something about it by creating a Java library that seems to address many of the shortcomings in the default JDK date classes. Pretty cool.

So using Joda Time you can implement the example from my date handling rant as:

DateTime start = new DateTime(2004, 2, 17, 0, 0, 0, 0);
DateTime end = new DateTime(2005, 2, 17, 0, 0, 0, 0);
Period period = new Period(start, end, PeriodType.weeks());
int numberOfWeeks = period.getWeeks();
System.out.println("numberOfWeeks = " + numberOfWeeks);

Well, that's a lot better than using the JDK date classes and close to the simplicity of the Python example. And it prints the correct answer, 52.

I did run into a slight issue at first. Notice in the above example I am using the version of the Period constructor that takes two ReadableInstants and a PeriodType. In this case I wanted to compute weeks so I used PeriodType.weeks() as the value of the argument. There is a constructor that takes only the two ReadableInstants which, according to the JavaDoc, is supposed to use the "standard" PeriodType and should include the "standard set of fields". The standard fields defined in PeriodType include: years, months, weeks, days, hours, minutes, seconds, and millis. Good enough, right?

Well, when I run the following sample code it prints out zero not 52 as I initially expected:

DateTime start = new DateTime(2004, 2, 17, 0, 0, 0, 0);
DateTime end = new DateTime(2005, 2, 17, 0, 0, 0, 0);
Period period = new Period(start, end); // uses standard PeriodType
int numberOfWeeks = period.getWeeks();
System.out.println("numberOfWeeks = " + numberOfWeeks); // prints zero!

In fact, the value of millis, seconds, minutes, hours, days, weeks, and months are all zero. Only year has a value and its value is one. Hmmm. After some more playing around I found out that the way Joda Time computes the period is actually pretty intelligent. Check out the following slightly modified example, which contains dates with different months, day, hour, minute, etc.

DateTime start = new DateTime(2004, 2, 17, 16, 30, 10, 546);
DateTime end = new DateTime(2005, 5, 10, 17, 45, 7, 344);
Period period = new Period(start, end);
System.out.println("period = " + period);

When run this prints: period = P1Y2M3W2DT1H14M56.798S. So it actually is smart enough to break apart the period into years, months, weeks, days, etc. That is pretty cool. And if you query the individual methods such as getDays() you get the values shown in the output string - 1 year, 2 months, 3 weeks, 2 days, 1 hour, 14 minutes, 56 seconds, and 798 milliseconds. So the values in Period must be taken together as defining a period, which makes sense since that is really how people tend to think about dates.

Joda Time also provides Interval and Duration classes in addition to Period. They all do slightly different things. But overall there are a ton of useful utilities in Joda Time and I plan to start using it straightaway on projects.

Date Handling in Java

Posted on February 16, 2005 by Scott Leberknight

There has been a thread going around an internal forum at work the past few days on calculating the difference between two dates in weeks, and how to implement that using Java. Of course since working with dates is Java is not at all fun, the code was very verbose and had lots of conversion from Strings to Dates to longs to perform subtraction on the time in milliseconds and then a bunch of basic math to convert milliseconds to weeks. Ugh. Since I've been dabbling a little in Python lately (that is, going through the tutorial very slowly whenever I get a few minutes here and there) I decided to see how this would be accomplished in Python. Here it is:

date_from = date(2004, 2, 17)
date_to = date(2005, 2, 17)
diff = date_to - date_from
diff_in_weeks = diff.days / 7
print diff_in_weeks

And of course the answer it prints is 52. So that was five lines of code including the print statement.

The equivalent code in Java might look something like the following:

GregorianCalendar fromCalendar = new GregorianCalendar(2004, 1, 17); // month is zero-based
long fromDate = fromCalendar.getTimeInMillis();

GregorianCalendar toCalendar = new GregorianCalendar(2005, 1, 17); // month is zero-based
long toDate = toCalendar.getTimeInMillis();

long diffInMillis = toDate - fromDate;
long diffInWeeks = diffInMillis / (1000 * 60 * 60 * 24 * 7); // lots of yuckiness here to convert millis to weeks
System.out.println(diffInWeeks);

So that's seven lines of code including the print statement. That's not that much of a difference, but the difference in readability is a rather large difference. In the Python version, we create two dates and subtract them to get a timedelta object which "represents a duration, the difference between two dates or times" according to the Python docs. We are now working with an object that inherently represents a difference between times and thus the code is very clean. On the other hand, in the Java code we have to convert from a Calendar to a Date to a long, compute the difference in a primitive type that does not inherently represent the difference between times, and do some yucky but still relatively simple math to convert from milliseconds to weeks.

And though I could have used the Date constructor that takes a year, month, and day I decided against that because that constructor and similar ones are deprecated. So I used a GregorianCalendar to obtain the date and then converted to a completely non-natural way to represent a date - a Java primitive long value.

The point is that dealing with dates, times, calendars, etc. in Java is not a very pleasing experience most of the time, and it is beyond me why the Java language does not directly support the notion of ranges, whether they are numeric or dates or times. At the least the Date or perhaps Calendar class could have included a diff() method that would allow you to compute the difference between two dates. Isn't this a pretty common thing to do in many applications? So of course everyone has to write their own version in some DateUtils class just like they have to create StringUtils classes to deal with omissions in String, such as checking if a string is empty or not empty, splitting and joining (prior to JDK 1.4), capitalizing, etc.

And to top it all off, the date and number formatting classes DateFormat and NumberFormat along with MessageFormat are not thread safe. That to me was a very poor design decision since now everyone needs to create their own wrappers or utility classes to be able to use a shared date formatter perhaps using a ThreadLocal to ensure thread safety. Until JDK 1.4 Sun didn't even bother to inform people of potential multithreading issues in the JavaDocs.

This is sort of a silly rant about relatively low-level details, but I think it points out that making your code read along the lines of your domain ensures readability and maintainability, since the code reflects the language of the problem domain and not the programming language. In this case the Python example more cleanly mirrors how you typically think about dates than the Java example, which makes for cleaner and more efficient code.

Business Week Lauds Firefox

Posted on February 16, 2005 by Scott Leberknight

The January 24, 2005 and February 7, 2005 issues of Business Week magazine both have articles on Firefox. The article in the former issue is entitled "The Gnat Nipping At Microsoft" and gives lots of kudos to the Mozilla Foundation in general, mentioning not only Firefox but also its Thunderbird mail client and forthcoming Sunbird calendar program. The latter article, entitled "Move Over, Internet Explorer", talks about some of Firefox's cool features like tabbed browsing and the ability to add plug-ins. When Business Week starts recognizing Firefox and encouraging business people to check out Firefox, I'd say Mozilla's efforts are starting to pay off in a big way. I hope it continues. My wife and I have already switched both of our parents over the Firefox and many people at work too. Now that I am thinking about it, I'm going to go make a donation to the Mozilla Foundation to support them as they continue to develop high-quality software.