19 December 2012

Date and Time in Java 8 (2)
If you like this content,
here's my book

This is the second part of a series on JSR-310; Part 1: The Basics. It's been translated into Serbo-Croatian here.

Timezones

The Local* classes that we looked at last time all ignored the complexity introduced by timezones. A timezone is a set of rules, corresponding to a region where the standard time is the same, there are about 40 of them. Each timezone has an offset from UTC, so it's time moves in sync with UTC but by some difference. Unfortunately the defined offset from UTC can vary due to changes in the rules. Timezones have two identifiers: abbreviated, eg "PLT", and longer, eg: "Asia/Karachi".

A time-zone offset is the period of time that a time-zone differs from Greenwich/UTC. Offsets can be resolved from a timezone at a particular point in time. For example in summer Paris is Greenwich/UTC + one hour. In winter it's Greenwich/UTC + two hours. You don't need to be aware of all the details, since JSR-310 abstracts these away for you, but you do need to understand the basic concept that there isn't a one-to-one mapping between offsets and timezones. When designing your application you should consider what scenarios are appropriate for using timezones and when offsets are appropriate.

Timezone Classes

There are a number of classes representing parts of the timezone system. These classes are all field-based classes, which is to say they can be queried for subfields, such as a Day, a Second or a ZoneOffset.

  • ZonedDateTime - A date and time with a fully qualified timezone. This can resolve an offset at any point in time. The general rule of thumb is that if you want to represent a date and time without relying the context of a specific server, then you should use ZonedDateTime.
  • OffsetTime - A time with a resolved offset.
  • OffsetDate - A date with a resolved offset.
  • OffsetDateTime - A date and time with a resolved offset. This is useful for serialising data into a database, which is discussed below, and also should be used as the serialisation format for logging timestamps if you have servers in different timezones.

There are also classes for modelling specific concepts within the domain of TimeZones:

  • ZoneId - an identifier for a region. Each ZoneId corresponds to some rules that define the timezone for that location. When designing your software if you consider throwing around a String such as "PLT" or "Asia/Karachi" then you should use this domain class instead. For example storing a user's preference for their timezone.
  • ZoneOffset - the period of time representing a difference between Greenwich/UTC and a timezone. This can be resolved for specific a ZoneId at a specific moment in time.

API

The API conventions that were outlined for Local* classes are also present in the field based timezone classes. These include:

Object creation using factory methods starting with 'of', 'from' or 'now'.

// You can specify the zone id when creating a zoned date time
ZoneId id = ZoneId.of("Europe/Paris");
ZonedDateTime zoned = ZonedDateTime.of(dateTime, id);

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

ZoneOffset offset = ZoneOffset.of("+2:00");

// Extract useful components
ZoneId.from(zoned);
OffsetDate.from(zoned);

OffsetTime time = OffsetTime.now();

Values accessed by get* methods, and new objects created by calling with* or plus* methods on an existing object.

// changes offset, whilst 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
  .withHours(3)
  .plusSeconds(2)
  .minusMinutes(4);

A well defined ordering comparison based on the timeline.

A powerful Adjuster mechanism in order to model business logic based manipulations.

ZonedDateTime zdt;

zdt = zoned.with(firstDayOfYear());
zdt = zoned.with(lastInMonth(TUESDAY));
zdt = zoned.with(time);

Databases and JDBC

ANSI SQL provides two types that are related to timezones - the misleading named "TIME WITH TIME ZONE" and "TIMESTAMP WITH TIME ZONE". Neither of these types actually store a timezone! They both store the offset. This is important because it means that you can't resolve the exact timezone after persistence. This means developers need to consider whether the appropriate choice is to store just the offset or whether they should also store the timezone as well.

It's worth noting that there will no public JDBC API changes in order to support JSR-310, merely to the spec. The existing generic setObject and getObject methods will be sufficient.

Existing Timezone classes

There is an existing timezone class in Java - java.util.TimeZone - but this isn't used by JSR-310 since all 310 classes are immutable, and timezone is mutable. There are also subtle incompatible issues to be aware of, specifically that the previous timezone implementation only uses 32bit data for storing timezones. This limits the supported values to between 1901-12-13T20:45:52Z and 2038-01-19T03:14:07Z. Obviously JSR-310 isn't limited by the restriction, and supports a wider range of timezones. The result is that outside of the aforementioned dates compatibility between the existing timezone classes and JSR-310 isn't guaranteed, but if you always use the new classes they will provide the correct timezone information for a wider range of dates than was previously possible.

Conclusion

I've looked at how timezones can be used, and when to consider OffsetDateTime and ZonedDateTime. In Part 3 I plan to talk about Periods, which are a way of representing the different between two dates or times.

You can follow the final stages of JSR-310 development by reading the mailing list or on the openjdk project, where the implementation work is being done and commits get emailed

Read More: Date and Time in Java 8 (1)



comments powered by Disqus