Introduction to Joda-Time

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

1. Introduction

Joda-Time is the most widely used date and time processing library, before the release of Java 8. Its purpose was to offer an intuitive API for processing date and time and also address the design issues that existed in the Java Date/Time API.

The central concepts implemented in this library were introduced in the JDK core with the release of the Java 8 version. The new date and time API is found in the java.time package (JSR-310). An overview of these features can be found in this article.

After the release of Java 8, authors consider the project to be mostly finished and advise to use the Java 8 API if possible.

2. Why Use Joda-Time?

The date/time API, before Java 8, presented multiple design problems.

Among the problems is the fact that the Date and SimpleDateFormatter classes aren’t thread-safe. To address this issue, Joda-Time uses immutable classes for handling date and time.

The Date class doesn’t represent an actual date, but instead, it specifies an instant in time, with millisecond precision. The year in a Date starts from 1900, while most of the date operations usually use Epoch time which starts from January 1st, 1970.

Also, the day, month and year offset of a Date is counterintuitive. Days start at 0, while month begins from 1. To access any of them, we must use the Calendar class. Joda-Time offers a clean and fluent API for handling dates and time.

Joda-Time also offers support for eight calendar systems, while Java offers just 2: Gregorian – java.util.GregorianCalendar and Japanese – java.util.JapaneseImperialCalendar.

3. Setup

To include the functionality of the Joda-Time library, we need to add the following dependency from Maven Central:

4. Library Overview

Joda-Time models the concept of date and time using the classes in the org.joda.time package.

Among those classes the most commonly used are:

LocalDate – represents a date without time

LocalTime – represents the time without the time zone

LocalDateTime – represents both the date and time without a time zone

Instant – represents an exact point in time in milliseconds from the Java epoch of 1970-01-01T00:00:00Z

Duration – represents the duration in milliseconds between 2 points in time

Period – similar to Duration, but allowing access to individual components of the date and time object, like years, month, days, etc.

Interval – represents the time interval between 2 instants

Other important features are the date parsers and formatters. These can be found in the org.joda.time.format package.

The calendar system and time zone specific classes can be found in the org.joda.time.chrono and org.joda.time.tz packages.

Let’s take a look at some examples in which we use the key features of Joda-Time to handle date and time.

5. Representing Date and Time

5.1. Current Date and Time

The current date, without time information, can be obtained by using the now() method from the LocalDate class:

LocalDate currentDate = LocalDate.now();

When we need just the current time, without date information, we can use the LocalTime class:

LocalTime currentTime = LocalTime.now();

To obtain a representation of the current date and time without considering the time zone, we can use LocalDateTime:

LocalDateTime currentDateAndTime = LocalDateTime.now();

Now, using currentDateAndTime, we can convert it to the other types of objects modeling date and time.

We can obtain a DateTime object (which takes into account the time zone) by using the method toDateTime(). When time is not necessary we can convert it to a LocalDate with the method toLocalDate(), and when we only need the time we can use toLocalTime() to obtain a LocalTime object:

Also, Joda-Time offers excellent integration with the Java Date and Time API. The constructors accept a java.util.Date object and also, we can use the toDate() method to return a java.util.Date object:

6. Working with Date and Time

6.1. Using Instant

An Instant represents the number of milliseconds from 1970-01-01T00:00:00Z until a given moment in time. For example, the current moment in time can be obtained using the default constructor or the method now():

Instant instant = new Instant();
Instant.now();

To create an Instant for a custom moment in time we can use either one of the constructors or use the methods ofEpochMilli() and ofEpochSecond():

Now that we know what Instant represents and how we can create one, let’s see how it can be used.

To compare to Instant objects we can use compareTo() because it implements the Comparable interface, but also we can use the Joda-Time API methods provided in the ReadableInstant interface which Instant also implements:

Now that we covered the Instant class let’s see some examples of how we can use Duration, Period and Interval.

6.2. Using Duration, Period and Interval

A Duration represents the time in milliseconds between two points in time or in this case it could be two Instants. We’ll use this when we need to add or subtract a specific amount of time to or from another Instant without considering chronology and time zones:

The main difference between Period and Duration is that Period is defined in terms of its date and time components (years, months, hours, etc.) and doesn’t represent an exact number of milliseconds. When using Period date and time calculations will consider the time zone and daylight saving.

For example, adding a Period of 1 month to the February 1st will result in the date representation of March 1st. By using Period the library will take into account leap years.

If we’re to use a Duration we the result wouldn’t be correct, because the Duration represents a fixed amount of time that doesn’t take into account chronology or time zones:

Period period = new Period().withMonths(1);
LocalDateTime datePlusPeriod = localDateTime.plus(period);

An Interval, as the name states, represents the date and time interval between two fixed points in time represented by two Instant objects:

Interval interval = new Interval(oneMinuteAgoInstant, instantNow);

The class is useful when we need to check whether two intervals overlap or calculate the gap between them. The overlap() method will return the overlapping Interval or null when they don’t overlap:

The difference between intervals can be calculated using the gap() method, and when we want to know whether the end of an interval is equal to the start of another interval we can use the abuts() method:

assertTrue(interval1.abuts(new Interval(
new Instant("2018-05-05T11:00:00.000"),
new Instant("2018-05-05T13:00:00.000"))));

6.3. Date and Time Operations

Some of the most common operations are to add, subtract and convert date and time. The library provides specific methods for each of the classes LocalDate, LocalTime, LocalDateTime, and DateTime. It’s important to note that these classes are immutable so that every method invocation will create a new object of its type.

Let’s take LocalDateTime for the current moment and try to change its value:

LocalDateTime currentLocalDateTime = LocalDateTime.now();

To add an extra day to currentLocalDateTime we use the plusDays() method:

LocalDateTime nextDayDateTime = currentLocalDateTime.plusDays(1);

We can also use plus() method to add a Period or Duration to our currentLocalDateTime:

Besides, doing calculations with date and time, we can also, set individual parts of the date or time. For example, setting the hour to 10 can be achieved using the withHourOfDay() method. Other methods that start with the prefix “with” can be used to set components of that date or time:

Another important aspect is that we can convert from a date and time class type to another. To do this, we can use specific methods provided by the library:

toDateTime() – converts LocalDateTime to a DateTime object

toLocalDate() – converts LocalDateTime to a LocalDate object

toLocalTime() – converts LocalDateTime to a LocalTime object

toDate() – converts LocalDateTime to a Java Date object

7. Working with Time Zones

Joda-Time makes it easy for us to work with different time zones and changed between them. We have the DateTimeZone abstract class which is used to represent all aspects regarding a time zone.

The default time zone used by Joda-Time is selected from the user.timezone Java system property. The library API lets us specify, individually for each class or calculation what timezone should be used. For example, we can create a LocalDateTime object

When we know that we’ll use a specific time zone across the whole application, we can set the default time zone:

DateTimeZone.setDefault(DateTimeZone.UTC);

From now one all the date and time operations, if not otherwise specified, will be represented in the UTC time zone.

To see all the available time zones we can use the method getAvailableIDs():

DateTimeZone.getAvailableIDs()

When we need to represent the date or time in a specific time zone we can use any of the classes LocalTime, LocalDate, LocalDateTime, DateTime and specify in the constructor the DateTimeZone object:

8. Conclusion

Joda-Time is a fantastic library that started with the main goal to fix the problems in the JDK regarding date and time operations. It soon became the de facto library for date and time handling and recently the main concepts from it were introduced in Java 8.

It’s important to note that the author considers it “to be a largely finished project” and recommends to migrate the existing code to use the Java 8 implementation.