This post is continues our review of the Date API that came with the release of Java 8. We are going to continue our concentration on classes that make working with dates/times very easy. Working with date objects in previous releases of Java was very challenging with respect to adding time or getting the difference between dates. Hopefully after looking at the classes we present here, your opinion of working with dates and times in Java will change. Specifically, we are going to take a look at the following classes:

  • Other classes to represent dates/times ZonedDateTime and OffsetDateTime
  • Getting the current snapshot in time with Instant
  • Using the Clock class to get system time but specify different time zones
  • Represent arbitrary number of days with the Period class
  • Represent arbitrary amount of hours with the Duration class

Zoned/Offset Dates and Times

In the last post we covered the LocalDateTime, LocalDate and the LocalTime classes. As the name suggests, these classes give the date and or time for a given locality with no time-zone or offset from UTC/Greenwhich time. Java 8 provides the ZonedDateTime class which provides date-times with a time-zone representation. Creating a ZonedDateTime instance can be done several ways, but here we will demonstrate using two of the many static factory methods:

 //Uses the system clock using the default time-zone.
 ZonedDateTime zdt = ZonedDateTime.now();
 //Displays as 2014-03-28T21:52:09.122-04:00[America/New_York]

 //Uses the system clock with the specified time-zone
 ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Europe/Dublin"));
 //Displays as 2014-03-29T01:45:34.605Z[Europe/Dublin]

Next we have the OffsetDateTime and the OffsetTime classes that represent date-time or time (respectively) with offsets from UTC/Greenwhich time. Here we show creating OffsetDateTime and OffsetTime using some of the other available static factory methods:

OffsetDateTime odt = OffsetDateTime.of(LocaDateTime.now(),ZoneOffset.of("-4"));
//Displays as 2014-03-28T22:30:28.911-04:00

OffsetTime ot = OffsetTime.ofInstant(Intant.now(),ZoneId.of("America/Los_Angeles"));
//Displays as 19:45:40.661-07:00

In the last example we see the use of the Instant class which we will introduce soon. It’s worth noting at this point that the ZondedDateTime,OffsetDateTime classes store time to nanosecond precision. Typically we would use the ZonedDateTime class when showing date-times to users and the OffsetDateTime class when interacting with systems.

Clock

The Clock class gives us the ability to get the current date/time from the system clock with a specific time-zone. Although there are date-time classes that have a now() method returning the current date-time, the system clock uses the default time-zone. The Clock class allows us to get the system time with a given time-zone. We can then plug the Clock instance into other classes where we want to get the current time using a given time-zone.

//Returns Clock with default time-zone
Clock default = Clock.systemDefaultZone();
//Clock with desired time-zone
Clock clock = Clock.system(ZoneId.of("America/Chicago"));

We can now re-work our previous example of creating an OffsetTime object using a Clock instance to set the desired time-zone:

Clock clock = Clock.system(ZoneId.of("America/Los_Angeles"));
......
public void someOperation(Clock clock){
     OffsetTime ot = OffsetTime.of(clock);
     ... some work involving the OffsetTime intance
}

The Clock class also allows us to specify how it ‘ticks’, meaning we can have the time returned from the Clock instance ticking on whole minutes or seconds

Clock wholeMinuteClock = Clock.tickMinutes(ZoneId.of("Europe/Athens"));
Clock wholeSecondClock = Clock.tickSeconds(ZonieId.of("Europe/Prauge"));

Instant

The Instant class is used to capture the current ‘instant’ in time. The Instant class is useful for obtaining event timestamps and also has nanosecond precision.

//Record the current moment on the time-line from the system clock
Instant now = Instant.now();
//Record the current instant with the given Clock instance
Instance now = Instant.now(clock)

The Instant class has several other methods for adjusting an Instant instance such as adding/subtracting time or converting to a OffsetDateTime or ZonedDateTime.

Period

The Period class represents an arbitrary amount of time in years, months, or days. Period objects can be particularly useful for adding/subtracting time to/from a date. For example:

    @Test
    public void test_add_days(){
        LocalDateTime today = LocalDateTime.parse("2014-03-12T19:36:33");
        Period sixDays = Period.ofDays(6);
        LocalDateTime nextWeek = today.plus(sixDays);
        assertThat(nextWeek.toString(),is("2014-03-18T19:36:33"));
    }

    @Test
    public void test_subtract_days(){
        LocalDate today = LocalDate.parse("2014-03-12");
        Period twoWeeks = Period.ofWeeks(2);
        LocalDate past = today.minus(twoWeeks);
        assertThat(past.toString(),is("2014-02-26"));
    }

The Period class also offers a static method Period.between that is great for determining elapsed time between dates:

    @Test
    public void test_period_between_dates(){
        LocalDate twins = LocalDate.parse("2003-11-18");
        LocalDate mayhem = LocalDate.parse("2009-06-01");
        Period timeBetween = Period.between(twins,mayhem);
        assertThat(timeBetween.getYears(),is(5));
        assertThat(timeBetween.getMonths(),is(6));
        assertThat(timeBetween.getDays(),is(14));
    }    

While the Period.between method requires types of LocalDate, the date-time classes in the java.time package offer a toLocalDate() method that returns a LocalDate from the date-time instance.

Duration

The Duration class represents arbitrary amounts of time in hours, minutes or seconds. The useage pattern for Duration is similar to that of the Period class. Here are examples of adjusting time objects using the Duration class:

    @Test
    public void test_add_time(){
        Duration oneHourThirtyMinutes = Duration.ofHours(1).plusMinutes(30);
        OffsetTime now = OffsetTime.parse("10:15:30-05:00");
        OffsetTime later = now.plus(oneHourThirtyMinutes);
        assertThat(later.toString(),is("11:45:30-05:00"));

        Duration threeHours = Duration.parse("PT3H");
        LocalTime twoPM = LocalTime.parse("14:00:00");
        LocalTime fivePM = twoPM.plus(threeHours);
        assertThat(fivePM.toString(),is("17:00"));
    }

    @Test
    public void test_subtract_time(){
        OffsetTime offsetTime = OffsetTime.parse("13:34:00+01:00");
        LocalTime earlier = LocalTime.parse("09:30:25");
        LocalTime later = LocalTime.parse("15:33:47");
        Duration timeSpan = Duration.between(earlier,later);
        OffsetTime adjustedOffsetTime = offsetTime.minus(timeSpan);
        assertThat(adjustedOffsetTime.toString(),is("07:30:38+01:00"));
    }

The Duration class also has a between method for determing the amount of time (hours based) between time objects:

    @Test
    public void test_time_between(){
        LocalTime earlier = LocalTime.parse("09:30:25");
        LocalTime later = LocalTime.parse("15:33:47");
        Duration timeSpan = Duration.between(earlier,later);
        assertThat(timeSpan.toString(),is("PT6H3M22S"));
        assertThat(LocalTime.MIDNIGHT.plus(timeSpan).toString(),is("06:03:22"));
    }

The Duration.between method requires types of Temporal. All of the time and date-time classes in the java.time package implement the Temporal interface.

Conclusion

This wraps up our brief tour of the new Date API in Java 8. Hopefully you can see the promise in the new Date API for working with dates and times. In future posts I plan to continue coverage of the new features found in Java 8. Thanks for your time.

Resources