Sunday, January 11, 2009

Fluent Calendar Builder

If you have ever worked with Java's java.util.Calendar interface, you understand the definition of pain. Don't get me wrong, java.util.Calendar is head and shoulders above java.util.Date or java.util.Time but it still hurts way too much to work with. Especially if you want to do something simple like instancing a Date/Calendar with tomorrow's date:

java.util.Date:
Date date = new Date(2009, 1, 12, 0,0,0);
System.out.println(date); <-- Fri Feb 12 00:00:00 MST 3909


java.util.Calendar:
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, 2009);
calendar.set(Calendar.MONTH, Calendar.JANUARY);
calendar.set(Calendar.DATE, 12);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
System.out.println(calendar.getTime()); <-- Mon Jan 12 00:00:00 MST 2009


As you can see Date just gets it wrong. First of all it starts the months at zero, there is no reason for a zero index other than bad habits carried over from pointer based languages (C,C++). Second, what's with the year 3909? Oh that's right, Date works with 2 digit years [ala millennial bug] so instead of 2009 you would need to set the year to 2009 - 1900 (Date's epoch) which is 109.

Calendar doesn't do any better with the month index starting at zero, but at least it gives you some static fields to prevent mistaking January with 1 (how silly of you using logic, 1 = January). Plus, Calendar does work correctly with 4 digit years. But, OMG what a pain, from 1 line of instantiation for Date to 7 lines for Calendar.

These are just the easiest pain points to point-out, there are dozens for both Date and Calendar. Review this pdf to see more problems. Why can't it be as easy as:
Calendar calendar = Calendar.tomorrow().midnight();

Well it can be with a little work. Enter the Fluent Interface pattern. It can be used as like a DSL and it can ease the Date/Calendar pain immensely. By wrapping the instantiation of Calendar in a DSL Fluent Interface, CalendarBuilder I was able to do some amazing things like:
Calendar id4 = CalendarBuilder.builder().july().fourth().year(1776).build();
Calendar tomorrowNoon = CalendarBuilder.builder().tomorrow().noon().build();


Simple as Date but with the power of Calendar - much nicer. Be warned, this still doesn't remove the other failings of Calendar but it does make it less painful than pulling nose hairs but still as painful as hitting your silly bone, a little tingly and sometimes you even laugh. Don't expect the problems with Date/Calendar to get resolved any time soon. Date was Java's first attempt at working with temporal data but it was very flawed. Calendar was introduced in Java 1.1 and it fixed some of Date's ills but introduced a whole new set of problems. Now JSR-310 is set up to fix all of Date and Calendar's flaws but I'm not holding my breath. Some of the smartest people around created Date and Calendar and they couldn't get it right. I expect JSR-310 to make some good progress toward a better temporal model but I suspect that it will keep some warts and sprout several of its own. But if you have to work with Calendar I would strongly suggest a Fluent Calendar Builder until JSR-310 rides in to the rescue.

No comments: