Joda-Time: The Future of Java Date/Time

David Kieras Java, Technology Snapshot 12 Comments

So how often have you had to work with date/time functions in Java? I mean really WORK with them? I’m not talking about simply setting the current time by instantiating a java.util.Date object or getting a Calendar instance, maybe getting a java.sql.Timestamp from a ResultSet. No, I’m talking about manipulating dates, working in different time zones, and working with dates and date ranges with respect to Daylight Savings Time. If you have had to do these things with native Java classes, you’ve probably just groaned.

Think about creating a date representing the Apollo 11 moon landing, which occurred on July 20, 1969 at 4:17 pm US/Eastern time. How would you do that with a Calendar object? You might expect one of these Calendars to work:

Calendar calendar = Calendar.getInstance();
TimeZone tz = TimeZone.getTimeZone("US/Eastern");
Calendar calendar2 = Calendar.getInstance(tz);
System.out.println("TimeZone is " + tz.getDisplayName());
calendar.set(1969, 7, 20, 16, 17);
calendar2.set(1969, 7, 20, 16, 17);
SimpleDateFormat sdf= new SimpleDateFormat("M/dd/yyyy h:mm a zzz");
System.out.println("Calendar moon landing eastern is " + sdf.format(calendar.getTime()));
System.out.println("Calendar2 moon landing eastern is " + sdf.format(calendar2.getTime()));
//now change the time zone and see what the time looks like:
System.out.println("Calendar moon landing central is " + sdf.format(calendar.getTime()));

Interestingly, here is the output:

TimeZone is Eastern Standard Time
Calendar moon landing eastern is 8/20/1969 3:17 PM CDT
Calendar2 moon landing eastern is 8/20/1969 3:17 PM CDT
Calendar moon landing central is 8/20/1969 3:17 PM CDT

So…we got the current date-time to US/Eastern, then set the year to 1969, the month to the 7th, the date to the 20th, and the time to 16:17 (4:17pm) So then why did we get 8/20/1969 US/Central? First, the most obvious, is that Calendar months are zero-based, which is why July, while it is the 7th month of the year, is month #6…and while month #7 in Java is the 8th month of the year, August. This has been a problem since, well, the beginning. Definitely not intuitive, but it can be worked around. Sure, we could have “known” this or coded for this and set the month to 6 to represent the 7th month. But more interestingly, the time zone was Central time! Even though the time was adjusted correctly, wouldn’t you have expected that, since we changed the time zone to US/Eastern, that our Calendar object was set to US/Eastern? Furthermore, after adjusting the time zone back to US/Central, the time never changed? Consider the Javadoc for setTimeZone:

/* Recompute the fields from the time using the new zone. This also
* works if isTimeSet is false (after a call to set()). In that case
* the time will be computed from the fields using the new zone, then
* the fields will get recomputed from that. Consider the sequence of
* calls: cal.setTimeZone(EST); cal.set(HOUR, 1); cal.setTimeZone(PST).
* Is cal set to 1 o'clock EST or 1 o'clock PST? Answer: PST. More
* generally, a call to setTimeZone() affects calls to set() BEFORE AND
* AFTER it up to the next call to complete().

Huh? Wait…okay, so setTimeZone affects calls to set(), but doesn’t change the existing Calendar time? So if we wanted to change the time zone we are working with, we have to adjust all the time fields too…but a SimpleDateFormatter still would show it as the current time zone. You see just how complex this is starting to get. And the more complex, the more unit tests are necessary to make sure your code is working properly.

Enter Joda-Time; a Java replacement date/time framework that aims to make time/date handling in Java much more intuitive and, by association, less error prone. Let’s take our previous example and do the exact same thing in JodaTime. The code looks like this:

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 = " + dateTimeString + " --> 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

Fantastic! Less code, and it worked more intuitively. We were able to create a date/time object in a different time zone, and then change it to another time zone, and all the other fields adjusted to match.

Another important aspect of Joda-Time is its ability to work more effectively with Daylight Savings Time. Take for example March 13, 2011, which is the “leap forward” date of Daylight Savings time. At 2:00 am in the United States, the time advances to 3:00 am. Therefore, there is no 2:00-2:59 am timeframe on that day. So let’s look what happens when we create an invalid time in JDK 6:

Calendar calInvalid = cal.getInstance();
calInvalid.set(2011, 02, 13, 2, 5);
System.out.println("Calendar invalid --> " + df2.format(calInvalid.getTime()));

We see this output:

Calendar invalid --> 2011-03-13T03:05:00.000

Notice that JDK6 adjusted the time for us to 3:05 am, because 2:05 actually was 3:05 on that day. Sounds reasonable, right? Well, what if you didn’t want the JDK to adjust for you and you wanted to know when the date specified was invalid? Enter Joda-Time:

DateTime dateSpringForward = null;
try {
    fmtDateTime = fmtDateTime.withZone(DateTimeZone.forID("US/Central"));
    dateSpringForward = fmtDateTime.parseDateTime("03/13/2011 2:05 AM");
    System.out.println("Spring forward date = " + dateSpringForward);
    } catch (IllegalArgumentException e) {
    System.err.println("Hey! Time doesn't exist! --> " + e.getMessage());

And our output is as expected:

Hey! Time doesn't exist! --> Cannot parse "03/13/2011 2:05 AM": Illegal instant due to time zone offset transition (America/Chicago)

At the risk of sounding like an infomercial, “wait…that’s not all!” While Joda-Time improves the predictability and intuitiveness of date/time programming, it helps make calculating dates a whole lot easier. Let’s say we want to know the days between two dates. Joda gives you a few nice features. First, it provides a DateMidnight class. No need to adjust a time to set the time to midnight, potentially accidentally changing some other field.

DateTimeFormatter fmtDate = DateTimeFormat.forPattern("M/dd/yyyy");
DateMidnight startMidnight = new DateMidnight(fmtDate.parseDateTime("02/13/2011"));
DateMidnight endMidnight = new DateMidnight(fmtDate.parseDateTime("02/15/2011"));

Joda then provides an Interval class, which is very convenient:

//An interval is inclusive for the start date, and up to but non-inclusive of the end date
Interval interval = new Interval(startMidnight, endMidnight);
System.out.println("Here's the interval: " + interval);
//traditionally, we could calculate the number of days this way:
System.out.println("Number of days: " + interval.toDurationMillis()/(1000 * 60 * 60 * 24)); //1000ms/sec*60 secs/min * 60 mins/hr * 24 hrs/day

The output looks like this:

Number of days: 2

However, this method fails when Daylight Savings is being observed, because the milliseconds don’t always add up according to that simple formula! Let’s take March 13th again:

DateMidnight dstStartMidnight = new DateMidnight(fmtDate.parseDateTime("03/13/2011"));
DateMidnight dstEndMidnight = new DateMidnight(fmtDate.parseDateTime("03/15/2011"));
Interval dstInterval = new Interval(dstStartMidnight, dstEndMidnight);
System.out.println("Here's the DST interval: " + dstInterval);
System.out.println("Number of days in DST range, wrong way: " + dstInterval.toDurationMillis()/(1000 * 60 * 60 * 24)); //1000ms/sec*60 secs/min * 60 mins/hr * 24 hrs/day

Uh-oh! 47 hours is only one day?

Number of days in DST range, wrong way: 1

What we really want to do is use the Joda Days class for this…why reinvent the wheel with an overly simplified calculation?

Days days = Days.daysIn(dstInterval);
System.out.println("Number of days in DST range, right way: " + days.getDays());

Which provides the proper number of days:

Number of days in DST range, right way: 2

There are plenty of ways to convert between standard Calendar and Date objects and Joda-Time objects; here are a few examples:

java.sql.Date sqlDate = rs.getDate("dateField");
java.sql.Timestamp sqlTimeS = rs.getTimestamp("timestampField");
LocalDate localDate = new LocalDate(sqlDate.getTime());
LocalDateTime localDateTime = new LocalDateTime(sqlTimeS.getTime());
System.out.println("JodaLocalDate = " + localDate.toString() + " --> new SQLDate = " + new java.sql.Date(localDate.toDateTimeAtCurrentTime().getMillis()));
System.out.println("JodaLocalDateTime = " + localDateTime.toString() + " --> new SQLTimestamp = " + new java.sql.Timestamp(localDateTime.toDateTime().getMillis()));
Calendar cal = Calendar.getInstance();
LocalDate calDate = new LocalDate(cal.getTimeInMillis());
LocalDateTime calDateTime = new LocalDateTime(cal.getTimeInMillis());
//first, we need to create non-local dateTimes...these assume the current system timezone, etc.
DateTime jodaDate = new DateTime(calDate.toDateMidnight());
DateTime jodaDateTime = new DateTime(calDateTime.toDateTime().getMillis());
Calendar calendarDateOnly = jodaDate.toCalendar(Locale.US);
Calendar calendarDateTime = jodaDateTime.toCalendar(Locale.US);
calDateString = df2.format(calendarDateOnly.getTime());
calDtString = df2.format(calendarDateTime.getTime());

And this is only the tip of the iceberg for Joda-Time.

One very good resource, in addition to the Joda-Time web site, is the IBM Developerworks article:

It’s important to note that Joda-Time is the basis for a new date/time API to be included in Java: and
Simply put, this is a framework with a future.

— David Kieras,

About the Author
David Kieras

David Kieras

Share this Post

Comments 12

  1. djkieras

    One of my colleagues pointed out that in my first example, the reason that the dates displayed the wrong time zone and time between Central and Eastern time zones was a result of not setting the TimeZone on the SimpleDateFormatter. Shame on me for missing that! However, if you are reusing SimpleDateFormatters, this could show up as a problem. It’s not enough that SimpleDateFormatters are not thread-safe, now it’s one more place you have to worry about setting time values.

  2. keyholesoftware

    There have been some interesting comments on the Reddit submission associated with this blog post. See them here:


    bobindashadows: If I ever used a normal Java Date/Time class instead of Joda at work, I’d probably be publicly flogged.

    G_Morgan: I can’t believe the debate over Joda-time is still on going. Java time has been broken forever. Hell it is so broken that I’d have introduced it in a minor version patch.

    johnwaterwood: Joda time is really the only thing that makes sense for handling time in Java. This should have taken the place of that Date/Calendar crap a long time ago. Luckily there’s hope this will end up in JDK 8.

    IN REPLY to johnwaterwood:
    kpthunder: You may also be interested in Date4J. Joda time is not without its issues.

    I think one of the problems the author has with bundled Date/Calendar is that Simple/DateFormat objects have their own internal timezone for printing/parsing. So they print any supplied Calendar in the default timezone if their internal timezone is not adjusted explicitly. This is very unintuitive design which is often overlooked. But once you get used to it’s quite possible to work with standard java date/time libraries.


    int getMonthOfYear(long millis, int year) {
    // Perform a binary search to get the month. To make it go even faster,
    // compare using ints instead of longs. The number of milliseconds per
    // year exceeds the limit of a 32-bit int’s capacity, so divide by
    // 1024. No precision is lost (except time of day) since the number of
    // milliseconds per day contains 1024 as a factor. After the division,
    // the instant isn’t measured in milliseconds, but in units of
    // (128/125)seconds.

    int i = (int)((millis – getYearMillis(year)) >> 10);

    // There are 86400000 milliseconds per day, but divided by 1024 is
    // 84375. There are 84375 (128/125)seconds per day.

    ? ((i < 182 * 84375)
    ? ((i < 91 * 84375)
    ? ((i < 31 * 84375) ? 1 : (i < 60 * 84375) ? 2 : 3)
    : ((i < 121 * 84375) ? 4 : (i < 152 * 84375) ? 5 : 6))
    : ((i < 274 * 84375)
    ? ((i < 213 * 84375) ? 7 : (i < 244 * 84375) ? 8 : 9)
    : ((i < 305 * 84375) ? 10 : (i < 335 * 84375) ? 11 : 12)))
    : ((i < 181 * 84375)
    ? ((i < 90 * 84375)
    ? ((i < 31 * 84375) ? 1 : (i < 59 * 84375) ? 2 : 3)
    : ((i < 120 * 84375) ? 4 : (i < 151 * 84375) ? 5 : 6))
    : ((i < 273 * 84375)
    ? ((i < 212 * 84375) ? 7 : (i < 243 * 84375) ? 8 : 9)
    : ((i < 304 * 84375) ? 10 : (i < 334 * 84375) ? 11 : 12)));

    IN REPLY to AlyoshaV:
    Yep, most people don't consider how complicated that is.

    IN REPLY to AlyoshaV:
    yash3ahuja: Holy (expletive)

    IN REPLY to AlyoshaV:

    Just so people know this isn't a joke, this is Joda's actual implementation for Gregorian/Julian chronology, aptly found in Here's the code on some random website.

    Seriously? Is it really that much faster than a simple switch statement?

    IN REPLY to grauenwolf:

    I'll assume by switch statement you mean a bunch of if-elseifs, since this code is checking ranges, not equality. Since there's 24 cases. log_2(24) is ~4.6. Plus, just looking at the code, you do either 4 or 5 comparisons to get your answer.

    If you had an if-elseif for the leap-year case and an if-elseif for the non-leap-year case, you'd be doing 2-13 comparisons.

    IN REPLY to bobindashadows:

    Of that’s right, Java still doesn’t have a switch statement that isn’t retarded. Never mind.

  3. Daniel B. Chapman

    This is a great alternative, but the key problem with Java Date/Time is developers making the mistake –as with the setCalendar(TimeZone) calls above– of assuming the date has a time zone. Dates are UTC in Java and once you wrap your head around that it isn’t difficult to understand.

    Yes, the Java Calendar class is obtuse when you begin, but when you need to do something complex it is also very easy to understand. 95% of these “bugs” can be fixed by running a server in UTC and remembering that you’re working with UTC.

    UTC rants against business developers aside. . . its a nice library.

  4. Pingback: ThreeTen: The Ultimate Future of Java Date/Time…In Progress « Keyhole Software

  5. web design

    Can I just say what a relief to find someone who actually knows what they’re talking about. You definitely know how to bring an issue to light and make it important. More people need to read this and understand this side of the story.

Leave a Reply