Ticking over

Java SE 8 Date and Time: Why do we need a new date and time library?

BenEvansandRichardWarburton
clocks

Ben Evans and Richard Warburton outline the reasons why decent support for the date and time use cases of everyday devs is so darn important.

A long-standing bugbear of Java developers has been the
inadequate support for the date and time use cases of ordinary
developers.

For example, the existing classes (such
as java.util.Date and SimpleDateFormatter)
aren’t thread-safe, leading to potential concurrency issues for
users—not something the average developer would expect to deal with
when writing date-handling code.

Some of the date and time classes also exhibit quite poor API
design. For example, years
in java.util.Date start at 1900, months start at
1, and days start at 0—not very intuitive.

These issues, and several others, have led to the popularity of
third-party date and time libraries, such as Joda-Time.

In order to address these problems and provide better support in
the JDK core, a new date and time API, which is free of these
problems, has been designed for Java SE 8.

The project has been led jointly by the author of Joda-Time
(Stephen Colebourne) and Oracle, under JSR 310, and will appear in
the new Java SE 8 package java.time.

Core Ideas

The new API is driven by three core ideas: 

  • Immutable-value classes. One of the
    serious weaknesses of the existing formatters in Java is that they
    aren’t thread-safe. This puts the burden on developers to use them
    in a thread-safe manner and to think about concurrency problems in
    their day-to-day development of date-handling code. The new API
    avoids this issue by ensuring that all its core classes are
    immutable and represent well-defined values.
  • Domain-driven design. The new API models
    its domain very precisely with classes that represent different use
    cases
    for Date and Time closely.
    This differs from previous Java libraries that were quite poor in
    that regard. For example,java.util.Date represents an
    instant on the timeline—a wrapper around the number of
    milli-seconds since the UNIX epoch—but if you
    call toString(), the result suggests that it has a
    time zone, causing confusion among developers.

    This emphasis on domain-driven design offers long-term benefits
    around clarity and understandability, but you might need to think
    through your application’s domain model of dates when porting from
    previous APIs to Java SE 8.

  • Separation of chronologies. The new API
    allows people to work with different calendaring systems in order
    to support the needs of users in some areas of the world, such as
    Japan or Thailand, that don’t necessarily follow ISO-8601. It does
    so without imposing additional burden on the majority of
    developers, who need to work only with the standard
    chronology. 

LocalDate and LocalTime

The first classes you will probably encounter when using the new
API are LocalDate and LocalTime.
They are local in the sense that they represent date and time from
the context of the observer, such as a calendar on a desk or a
clock on your wall. There is also a composite class
called LocalDateTime, which is a pairing
of LocalDate and LocalTime

The existing classes aren’t thread-safe, leading to potential
concurrency issues for users—not something the average
developer would expect.

Time zones, which disambiguate the contexts of different
observers, are put to one side here; you should use these local
classes when you don’t need that context. A desktop JavaFX
application might be one of those times. These classes can even be
used for representing time on a distributed system that has
consistent time zones.

Creating Objects

All the core classes in the new API are constructed by fluent
factory methods. When constructing a value by its constituent
fields, the factory is called of; when converting
from another type, the factory is called from. There
are also parse methods that take strings as parameters. See Listing
1.

LocalDateTime timePoint = LocalDateTime.now(
    );     // The current date and time
LocalDate.of(2012, Month.DECEMBER, 12); // from values
LocalDate.ofEpochDay(150);  // middle of 1970
LocalTime.of(17, 18); // the train I took home today
LocalTime.parse("10:15:30"); // From a String

Listing 1 

Standard Java getter conventions are used in order to obtain
values from Java SE 8 classes, as shown in Listing 2. 

LocalDate theDate = timePoint.toLocalDate();
Month month = timePoint.getMonth();
int day = timePoint.getDayOfMonth();
timePoint.getSecond();

Listing 2 

You can also alter the object values in order to perform
calculations. Because all core classes are immutable in the new
API, these methods are called with and return
new objects, rather than using setters (see Listing 3). There are
also methods for calculations based on the different
fields. 

// Set the value, returning a new object
LocalDateTime thePast = timePoint.withDayOfMonth(
    10).withYear(2010);

/* You can use direct manipulation methods, 
    or pass a value and field pair */
LocalDateTime yetAnother = thePast.plusWeeks(
    3).plus(3, ChronoUnit.WEEKS);

Listing 3 

The new API also has the concept of
an 
adjuster—a block of code that can be
used to wrap up common processing logic. You can either write
WithAdjuster, which is used to set one
or more fields, or a 
PlusAdjuster, which
is used to add or subtract some fields. Value classes can also act
as adjusters, in which case they update the values of the fields
they represent. Built-in adjusters are defined by the new API, but
you can write your own adjusters if you have specific business
logic that you wish to reuse. See Listing 4. 

import static java.time.temporal.TemporalAdjusters.*;

LocalDateTime timePoint = ...
foo = timePoint.with(lastDayOfMonth());
bar = timePoint.with(previousOrSame(ChronoUnit.WEDNESDAY));

// Using value classes as adjusters
timePoint.with(LocalTime.now()); 

Listing 4 

Truncation

The new API supports different precision time points by offering
types to represent a date, a time, and date with time, but
obviously there are notions of precision that are more fine-grained
than this.

The truncatedTo method exists to support such
use cases, and it allows you to truncate a value to a field, as
shown in Listing 5. 

LocalTime truncatedTime = time.truncatedTo(ChronoUnit.SECONDS);

Listing 5 

Time Zones

The local classes that we looked at previously abstract away the
complexity introduced by time zones. A time zone is a set of rules,
corresponding to a region in which the standard time is the same.
There are about 40 of them. Time zones are defined by their offset
from Coordinated Universal Time (UTC). They move roughly in sync,
but by a specified difference.

Time zones can be referred to by two identifiers: abbreviated,
for example, “PLT,” and longer, for example, “Asia/Karachi.” When
designing your application, you should consider what scenarios are
appropriate for using time zones and when offsets are
appropriate.

  • ZoneId is an identifier for a region (see Listing
    6). Each 
    ZoneId corresponds to some rules that define the
    time zone for that location. When designing your software, if you
    consider throwing around a string such as “PLT” or “Asia/Karachi,”
    you should use this domain class instead. An example use case would
    be storing users’ preferences for their time zone.
// You can specify the zone id when creating a zoned date time
ZoneId id = ZoneId.of("Europe/Paris");
ZonedDateTime zoned = ZonedDateTime.of(dateTime, id);
assertEquals(id, ZoneId.from(zoned));

Listing 6
  • ZoneOffset is the period of time representing a
    difference between Greenwich/UTC and a time zone. This can be
    resolved for a specific 
    ZoneId at a specific moment in time, as shown in
    Listing 7.  
ZoneOffset offset = ZoneOffset.of("+2:00");

Listing 7

Time Zone Classes 

  • ZonedDateTime is a date and time with a fully
    qualified time zone (see Listing 8). This can resolve an offset at
    any point in time. The rule of thumb is that if you want to
    represent a date and time without relying on the context of a
    specific server, you should use ZonedDateTime.
     

ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]");

Listing 8

 

OffsetDateTime is a date and time with a resolved offset. This is useful for serializing data into a database and also should be used as the serialization format for logging time stamps if you have servers in different time zones.

OffsetTime is a time with a resolved offset, as shown in Listing 9. 

OffsetTime time = OffsetTime.now();
// changes offset, while keeping the same point on the timeline
OffsetTime sameTimeDifferentOffset = time.withOffsetSameInstant(
    offset);
// changes the offset, and updates the point on the timeline
OffsetTime changeTimeWithNewOffset = time.withOffsetSameLocal(
    offset);
// Can also create new object with altered fields as before
changeTimeWithNewOffset
 .withHour(3)
 .plusSeconds(2);

Listing 9

There is an existing time zone class in
Java—java.util.TimeZone—but it isn’t used by Java SE 8
be-cause all JSR 310 classes are immutable and time zone is
mutable.

Periods

Period represents a value such as “3 months
and 1 day,” which is a distance on the timeline. This is in
contrast to the other classes we’ve looked at so far, which have
been points on the timeline. See Listing 10. 

// 3 years, 2 months, 1 day
Period period = Period.of(3, 2, 1);

// You can modify the values of dates using periods
LocalDate newDate = oldDate.plus(period);
ZonedDateTime newDateTime = oldDateTime.minus(period);
// Components of a Period are represented by ChronoUnit values
assertEquals(1, period.get(ChronoUnit.DAYS)); 

Listing 10

Java SE 8 will ship with a new date and time API in
java.time
 that offers greatly improved safety and
functionality for developers. The new API models the domain well,
with a good selection of classes for modeling a wide variety of
developer use cases.

Durations

Duration is a distance on the timeline
measured in terms of time, and it fulfills a similar purpose
to Period, but with different precision, as shown in
Listing 11. 

// A duration of 3 seconds and 5 nanoseconds
Duration duration = Duration.ofSeconds(3, 5);
Duration oneDay = Duration.between(today, yesterday);

Listing 11 

It’s possible to perform normal plus, minus, and “with”
operations on a Duration instance and also to
modify the value of a date or time using
the Duration.

Chronologies

In order to support the needs of developers using non-ISO
calendaring systems, Java SE 8 introduces the concept of
Chronology, which represents a calendaring system
and acts as a factory for time points within the calendaring
system. There are also interfaces that correspond to core time
point classes, but are parameterized by:

Chronology:
ChronoLocalDate
ChronoLocalDateTime
ChronoZonedDateTime

These classes are there purely for developers who are working on
highly internationalized applications that need to take into
account local calendaring systems, and they shouldn’t be used by
developers without these requirements. Some calendaring systems
don’t even have a concept of a month or a week and calculations
would need to be performed via the very generic field API.

The Rest of the API

Java SE 8 also has classes for some other common use cases.
There is the MonthDay class, which contains a
pair of Month and Day and is
useful for representing birthdays.
The YearMonth class covers the credit card start
date and expiration date use cases and scenarios in which people
have a date with no specified day.

JDBC in Java SE 8 will support these new types, but there will
be no public JDBC API changes. The existing
generic setObject and getObject methods
will be sufficient.

These types can be mapped to vendor-specific database types or
ANSI SQL types; for example, the ANSI mapping looks like Table
1.

ANSI SQL Java SE 8
DATE LocalDate
TIME LocalTime
TIMESTAMP LocalDateTime
TIME WITH TIMEZONE OffsetTime
TIMESTAMP WITH TIMEZONE OffsetDateTime

Table 1

Conclusion

Java SE 8 will ship with a new date and time API
in java.time that offers greatly improved safety
and functionality for developers. The new API models the domain
well, with a good selection of classes for modeling a wide variety
of developer use cases.

Originally published in the January/February 2014 issue
of 
Java
Magazine
Subscribe today.

(1) Originally published in the
 January/February 2014 Edition of Java
Magazine 

(2) Copyright © [2014] Oracle.



 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

Author

BenEvansandRichardWarburton

All Posts by BenEvansandRichardWarburton

Comments
comments powered by Disqus