Java.Time

A new new infrastructure for time and date handling.

Actually, it's not all that new, it's an evolution of the popular Joda-Time library, accepted into the JDK as JSR-310. Stephen Colebourne, who wrote Joda-Time, is the principal for the JSR.

What's in it

Terms like "date" and "time" are overloaded, so one aim is to clearly define what terms mean (rule 3).

  • Days - solar days
  • Date - label for a day
  • Time - time within a day (clock face)
  • Clock - source of ticks
  • Instant - a point on the timeline
  • Chronology - how is the timeline divided into eras, years, months, days

By these terms, a java.util.Date is not a date at all.

What about Joda-Time?

It's not going away. And if you're not using Java8 or planning to upgrade, you can and probably should go on using it.

However, it's interesting to look at the differences, they can demonstrate perceived deficiencies.

Sometimes they point to changed perceptions in common practices. For example, all java.time value objects are immutable; Joda-Time has mutable versions.

Core concepts

DateTime is gone

An instant is a point on the timeline, independent of any chronology

A date and time can be used to find an instant- if you have enough context, you can interconvert.

A date, time and zone offset

Joda-Time has Instant, but have you ever used it? DateTime implements ReadableInstant, so basically does everything an Instant does anyway.

// When is this exactly?
new org.joda.time.DateTime(2015, JANUARY, 14, 2, 3, 4).toMillis()

An instant is the "millis since epoch" (although now to nanosecond precision)

// Provide all the information
java.time.LocalDateTime.of(2015, JANUARY, 14, 2, 3, 4)
                       .toInstant(ZoneOffset.UTC)
// equivalent:
java.time.OffsetDateTime.of(java.time.LocalDate.of(2015, JANUARY, 14),
                            java.time.LocalTime.of(2, 3, 4),
                            ZoneOffset.UTC)
                        .toInstant()

OffsetDateTime or ZonedDateTime essentially replace DateTime

Instant replaces using a long (please...)

ISO chronology

"Everyone knows" there are 12 months in a year etc., right?

That's not actually always the case. Other chronologies work differently. In java.time, classes like LocalDateTime are definitely using ISO chronology, though. In Joda-Time, theoretically not...


((org.joda.time.DateTime) datetime).getMonthOfYear() == 13;
  // it can happen
((java.time.LocalDateTime) datetime).getMonthValue() == 13;
  // never happens
((java.time.LocalDateTime) datetime).getMonth() == Month.DECEMBER;
  // now months can be an enum
					

Fluent, extensible

Help to make working with date/time objects understandable

Period is a date-based amount (4 days, a week)

Duration is a time-based amount (2 minutes, 48 hours)

These are examples of temporal amounts.

dateTime.plus(Period.ofMonths(9));
dateTime.minus(Duration.ofSeconds(20));
instant.minus(Duration.ofHours(1));

TemporalAdjuster objects represent transformations

LocalDate.now().with(TemporalAdjusters.*/next(DayOfWeek.TUESDAY));
date.with(lastDayOfMonth());
date.with(lastDayOfQuarter());
twoDaysLater = TemporalAdjusters.ofDateAdjuster(date -> date.plusDays(2));
ChronoUnit.DAYS.between(then, now)
Year.of(2015).atMonth(Month.JANUARY).atEndOfMonth().atStartOfDay();

(It turns out that some days don't have a midnight...)

Time zones

java.util.TimeZone represents a zone offset and works out DST rules

java.time splits this into:

ZoneId
identifies a time zone (e.g. "Europe/London")
ZoneRules
rules for shifting DST
ZoneOffset extends ZoneId
just the offset, -18 to +18

// what does Joda-Time's DateTime.withTimeZone() do?
offsetDateTime.atZoneSameInstant(ZoneId.of("Europe/London"));
offsetDateTime.atZoneSimilarLocal(ZoneId.of("Europe/London"));
					

Clocks

When is Instant.now()?

Joda-Time has DateTimeUtils to allow overriding a fixed value

java.time defines a Clock class to provide the current instant and timezone, for injection


Clock.systemDefaultZone(); // default for LocalDateTime.now()
Clock.systemUTC(); // default for Instant.now()
Clock.system(ZoneId.of(someTzName)); // override default time zone
Clock.fixed(Instant.parse("2015-03-11T23:32:00Z"), ZoneOffset.UTC);
Clock.tickSeconds(UTC); // only tick 1 second at a time
Clock.tick(Clock.systemUTC(), Duration.ofMillis(1L)); // only 1ms
clock.instant(); clock.getZone(); // access the clock