by Mikhail Vorontsov
Joda Time library is an attempt to create a cleaner and more developer-friendly date/time library than an existing JDK
Date/Calendar/DateFormat ecosystem. This article provides a top level overview of Joda Time library from performance point of view. It may be also useful as a short introduction to Joda Time. I recommend reading java.util.Date, java.util.Calendar and java.text.SimpleDateFormat article before reading this one in order to better understand the performance of built-in JDK classes.
This article discusses Joda Time library versions 2.1 – 2.3.
26 Jan 2014: This is a major article rewrite – a major performance issue was found in Joda Time implementation.
Date/time storage classes
There are five general purpose date/time classes in this library:
DateTime – full replacement of
java.util.Calendar, supporting time zones and date/time arithmetic. This class is immutable.
MutableDateTime – mutable version of
LocalDate – immutable version of
DateTime containing only date fields. This class does not use a timezone after construction.
LocalTime – immutable version of
DateTime containing only time fields. This class does not use a timezone after construction.
LocalDateTime – immutable version of
DateTime not using a timezone.
All these classes contain 2 mandatory fields –
long with millis since epoch and a
Chronology object, containing all timezone and calendar system (Gregorian, Buddhist, etc.) related logic. Some of these classes contain 1-2 more fields. An instance of these classes occupy from 24 to 40 bytes.
Since these classes are based on the “machine” time – millis since epoch, we may expect the better performance on from/to
long conversions and worse performance on to/from date components (human time) conversions. In reality, Joda developers have made a series of clever optimizations which make it really fast even on the human time calculations.
Joda Time timezone offset calculation performance bug (ver 2.3)
Let’s start an updated version of this article from this point 🙂 When I wrote an original version of this article in late 2012, I noticed the poor performance of timezone based logic in Joda Time, but I thought that it was the common property of that library, so I wrote that “this logic is definitely worth optimizing in Joda Time”.
One year later I have written a more comprehensive set of tests which includes the new Java 8 date/time implementation (JSR-310). This test set has highlighted some weird inconsistencies between various date/time implementations. In particular I have noticed that creating a Joda
MutableDateTime from date components was 3-4 times faster than the same operation on date+time components. Nevertheless both were using the identical client logic. But there was a difference – date tests were using years from 1981 to 2000 (in a loop). DateTime tests were using 2013. This turned out to be a key to the problem.
MutableDateTime, as well as some other Joda Time classes are calculating timezone offset for the given “millis since epoch” values from time to time. It may be calculated more than once per client API call. Deep under the hood there is a
org.joda.time.tz.DateTimeZoneBuilder$PrecalculatedZone class with
getOffset method. This method looks up the transition table for the given timezone using binary search. If your timestamp is less or equal to the biggest timestamp in the table – you get it. Otherwise
org.joda.time.tz.DateTimeZoneBuilder$DSTZone.getOffset method is called for every offset you calculate. It uses daylight savings transition rules to calculate the latest transition and use it for offset calculation. Calculated values are not cached on this branch.
I have noticed this difference between years 2008 and 2009 in “Australia/Sydney” timezone. After that I ran the same test on all available timezones and found a list of zones in/around Australia and New Zealand with the same performance issue – offsets in 2009 were calculated much slower than in 2008. At the same time I have noticed that European timezones were slow in both 2008 and 2009. This led me to the conclusion.
Joda time ships with timezone rule source files in the
src/main/java/org/joda/time/tz/src directory. If you’ll take a look at “australasia” file and look for the “New South Wales” rule, you will see that 2 its last lines are:
Rule AN 2008 max - Apr Sun>=1 2:00s 0 -
Rule AN 2008 max - Oct Sun>=1 2:00s 1:00 -
This is the last fast year – 2008 (and I used 1st January for testing). After that I got more suspicious and opened “europe” file and got really worried. For example, Netherlands (Europe/Amsterdam) last rule belongs back to 1945:
Rule Neth 1945 only - Apr 2 2:00s 1:00 S
Rule Neth 1945 only - Sep 16 2:00s 0 -
The longer a country lives on a stable daylight saving rule, the more it gets penalized by Joda Time: it takes ~3.6 seconds to create 10M
MutableDateTime objects for year 2008 in “Europe/Amsterdam” timezone, ~3 seconds to create 10M
MutableDateTime objects for year 2010 in “Australia/Sydney” timezone (which also has to calculate its daylight savings transitions), but only ~1.2 seconds for year 2008 in Sydney (precalculated table).
So, I would like to ask Joda Time maintainers to consider prebuilding such transition tables during the timezone construction (at runtime) up to at least the current year plus a few years more.