Date4j - A Minimalistic Library for Handling Dates
The Java classes for handling dates have been mostly unchanged since JDK1.0 and suffer from well-known issues (for example January starts from 0 leading to many off-by-one bugs). A new JSR has been proposed to address those issues but it is still in Alpha version. Until it becomes stable many developers are using the Joda Time libraries which are similar (but not the same) to what the reference implementation of JSR-310 will become.
Date4j proposes another solution for handling dates in Java, but with a completely different scope from Joda Time. Here is the comparison:
|Number of classes: 140+||Number of classes < 10|
|Mutable and immutable classes||Only immutable classes|
|Focus on speed and features||Focus on simplicity and accuracy|
|Supports Gregorian, Coptic, Islamic, Buddhist e.t.c calendars||Only supports Gregorian dates|
|Can completely replace JDK Date classes||Works in tandem with some JDK Date classes|
|Supports millisecond resolutions||Supports nanosecond resolutions|
|Day "overflow" behaviour is fixed||Day "overflow" behaviour is configurable|
|Aimed at general purpose date manipulation||Aimed at dates manipulated by databases|
|Apache Licence 2.0||BSD Licence|
While at first glance Date4j only offers a minimal subset of the features found in Joda, it has two major advantages that Joda does not address.
Firstly, the developers of Date4j claim that a library should never truncate your date without reason. Joda supports only millisecond accuracy and this won't change probably in the future. Several databases however have greater resolution. For example the popular PostgreSQL database supports timestamps in microseconds. Date4j can handle these dates without any loss in precision.
The second point is the date "overflow" problem i.e. adding a period to a date that rolls it over to the next month. A simple example would be adding one month to 31st of March:
DateTime dt = new DateTime("2011-03-31"); DateTime result = dt.plusMonths(1); System.out.println(result.toString());
When Joda Time runs this it will print 30th of April which may or may not be what you expected.
Recognizing this ambiguity, Date4j gives you 4 ways of addressing it:
|2.||Last Day (Same as Joda Time)|
|4.||abort with an exception|
Here is demonstration of some of these cases (using Date4j instead of Joda):
DateTime dt1 = new DateTime("2011-03-31"); DateTime result1 = dt1.plus(0,1,0,0,0,0,DayOverflow.FirstDay); System.out.println(result1.toString()); //Prints 2011-05-01 (first of May) DateTime dt2 = new DateTime("2011-03-31"); DateTime result2 = dt2.plus(0,1,0,0,0,0,DayOverflow.LastDay); System.out.println(result2.toString()); //Prints 2011-04-30 (30th of April - same as Joda) DateTime dt3= new DateTime("2011-03-31"); DateTime result3 = dt3.plus(0,1,0,0,0,0,DayOverflow.Abort); System.out.println(result3.toString()); //Throws run time exception
This example shows the DayOverflow.Spillover option:
//Joda Time code DateTime dt = new DateTime("2010-12-31"); DateTime result = dt.plusMonths(2); System.out.println(result.toString()); //Prints 2011-02-28 (29th of February) //Date4j code DateTime dt1 = new DateTime("2010-12-31"); DateTime result1 = dt1.plus(0,2,0,0,0,0,DayOverflow.FirstDay); System.out.println(result1.toString()); //Prints 2011-03-01 (first of March) //Date4j code DateTime dt2 = new DateTime("2010-12-31"); DateTime result2 = dt2.plus(0,2,0,0,0,0,DayOverflow.LastDay); System.out.println(result2.toString()); //Prints 2011-02-28 (28th of February - Same as Joda) //Date4j code DateTime dt3= new DateTime("2010-12-31"); DateTime result3 = dt3.plus(0,2,0,0,0,0,DayOverflow.Spillover); System.out.println(result3.toString()); //Prints 2011-03-02 (2nd of March)
If your application needs high-accuracy and no loss of precision when handling database dates or you want to have maximum control on what happens with month spill overs, Date4j may be a useful library for your application. The source code is available as a direct download.
Joda comparison and DaysOverflow
JSR-310 also has the DateResolver concept, which is equivalent to DaysOverflow (but more flexible). Adding DaysOverflow/DateResolver to Joda-Time would be a relatively easy thing to do, but my focus is on 310 now, rather than Joda-Time (which is still maintained as necessary).
Stephen Colebourne, Joda-Time