A new Date and Time API for JDK 8

A new Date and Time API for JDK 8

Date and time handling in Java is a somewhat tricky part when you are new to the language. Time can be accessed via the static method System.currentTimeMillis() which returns the current time in milliseconds from January 1st, 1970. If you prefer to work with Objects instead you can use java.util.Date, a class whose methods are mostly deprecated in recent versions of Java. To work with time offsets, say add one month to a date,  there is java.util.GregorianCalendar. All in all, those methods described here are not very convenient to work with. Java 7 and below are lacking a good date and time API. The Joda Time library is a common drop-in if you need to work with date/time.

With JSR 310 (Java Specification Request) this is about to change. JSR 310 adds a new date, time, and calendar API to Java 8. The ThreeTen project provides a reference implementation to this new API and can already be utilized in current Java projects (I however recommend not to do this for production). As the README states:

The API is currently considered usable and accurate, yet incomplete and subject to change.
If you use this API you must be able to handle incompatible changes in later versions.

Building ThreeTen

Building the ThreeTen project is relatively easy. It requires both Git and Ant to be installed on your system.

git clone git://github.com/ThreeTen/threeten.git
cd threeten
ant

This will first fetch the most recent version of ThreeTen and then start the build process using ant. Note that building the library also requires either OpenJDK 1.6 or Oracle JDK 1.6.

JSR 310

The new API specifies a number of new classes which are divided into the categories of continuous and human time. Continuous time is based on Unix time and is represented as a single incrementing number.

Class Description
Instant A point in time in nanoseconds from January 1st 1970
Duration An amount of time measured in nanoseconds

Human time is based on fields that we use in our daily lifes such as day, hour, minute, and second. It is represented by a group of classes, some of which we will discuss in this article.

Class Description
LocalDate a date, without time of day, offset or zone
LocalTime the time of day, without date, offset or zone
LocalDateTime the date and time, without offset or zone
OffsetDate a date with an offset such as +02:00, without time of day or zone
OffsetTime the time of day with an offset such as +02:00, without date or zone
OffsetDateTime the date and time with an offset such as +02:00, without a zone
ZonedDateTime the date and time with a time zone and offset
YearMonth a year and month
MonthDay month and day
Year/MonthOfDay/DayOfWeek/... classes for the important fields
DateTimeFields stores a map of field-value pairs which may be invalid
Calendrical access to the low-level API
Period a descriptive amount of time, such as "2 months and 3 days"

In addition to the above classes three support classes have been implemented. The Clock class wraps the current time and date, ZoneOffset is a time offset from UTC and ZoneId defines a time zone such as 'Australia/Brisbane'.

Using the API

Getting the current time

The current time is represented by the Clock class. The class is abstract, so you can not create instances of it.  The systemUTC() static method will return the current time based on your system clock and set to UTC.

import javax.time.Clock;

Clock clock = Clock.systemUTC();

To use the default time zone on your system there also is systemDefaultZone().

Clock clock = Clock.systemDefaultZone();

The millis() method can then be used to access the current time in milliseconds from January 1st, 1970. This shows, that the Clock class and all subclasses are wrapped around System.currentTimeMillis().

Clock clock = Clock.systemDefaultZone();
long time = clock.millis();

Working with time zones

To work with time zones you need to import the ZoneId class. The class provides a method to get the default system time zone:

import javax.time.ZoneId;
import javax.time.Clock;

ZoneId zone = ZoneId.systemDefault();
Clock clock = Clock.system(zone);

As seen above, the ZoneId can then be used to get an instance of a Clock with that time zone. Other time zones can be accessed by their name, e.g.:

ZoneId zone = ZoneId.of("Europe/Berlin");
Clock clock = Clock.system(zone);

Getting human date and time

Working with a time represented in a single long variable is not what we wanted. We want to work with objects that represent human readable time. The LocalDate, LocalTime and LocalDateTime classes do just that.

import javax.time.LocalDate;

// The now() method returns the current DateTime
LocalDate date = LocalDate.now();
System.out.printf("%s-%s-%s", 
    date.getYear(), date.getMonthValue(), date.getDayOfMonth()
);

Doing calculations with times and dates

One of the most important functionalities of JSR-310 is that you can do calculations with dates and times. The API makes it very easy to do that.

import javax.time.LocalTime;
import javax.time.Period;
import static javax.time.calendrical.LocalPeriodUnit.HOURS;

Period p = Period.of(5, HOURS);
LocalTime time = LocalTime.now();
LocalTime newTime;
newTime = time.plus(5, HOURS);
// or
newTime = time.plusHours(5);
// or
newTime = time.plus(p);

Each class that represents human time implements the AdjustableDateTime interface. The interface requires the plus and the minus method that take a value and a PeriodUnit as argument.

Conclusion

This article gave a (very) brief introduction into the new date and time API that will ship with Java 8. The API seems to be very consistent and well thought through and provides many ways to interact with dates and times. Upon release of Java 8 the API will be moved from the javax.time package over to java.time, so there will be no conflict if you start using the current implementation.