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.