19 December 2012
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.
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.
There are also classes for modelling specific concepts within the domain of TimeZones:
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);
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.
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.
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.