Using JSR-310

Introduction

JSR-310 is a set of classes ear-marked for Java 7 which are a revamp of Java's built-in Date and Calendar classes. Given that JSR-310 is still at the proposal stage, some of the information here may change. Some key classes in JSR-310 include:

Class Description
ZonedDateTime ZonedDateTime is an immutable class representing calendar information, often viewed as year-month-day-hour-minute-second-zone.
LocalDate Immutable class representing a local date with no time or time zone
LocalTime Immutable class representing a time with no date or time zone
LocalDateTime Immutable class representing a local date and time with no time zone
Period Immutable class representing period consisting of the standard year, month, day, hour, minute, second and nanosecond units
Instant Immutable class representing an instantaneous point in time with nanosecond precision
MonthDay One of many partial date representations; this one representing a month and day but no year
Clock A facade for accessing the current time

If you are not using Java 7 by the time you read this, make sure you add a jsr-310-ri-yyyymmdd.jar jar to your classpath before running these examples. We used a hand built version based on trunk at 28-Sep-2008 running under Java 6 and have glossed over some differences (i.e. disguised the differences) so that it appears as we think it would under Java 7.

Accessing the current time

Accessing the current time is done using the Clock class. Several variations are shown here:

import javax.time.Clock
import javax.time.calendar.*
// use Clock.system() to get default time zone
def systemClock = Clock.system(TimeZone.UTC)

// some useful LocalDate instances
def today = systemClock.today()
def tomorrow = systemClock.tomorrow()
def yesterday = systemClock.yesterday()

// a ZonedDateTime instance
def todayZoned = systemClock.currentZonedDateTime()

println yesterday
println today
println tomorrow
println todayZoned

which will produce output like this:

2008-09-27
2008-09-28
2008-09-29
2008-09-28T03:24:36.781Z UTC

You can access fields of a LocalDate and ZonedDateTime instances using numerous properties:

println today.chronology
// just rely on the toString() of the year class
println today.year
// just rely on the toString() of the enums
[yesterday, today, tomorrow].each { day ->
    println day.monthOfYear
    println day.dayOfYear
    println day.dayOfMonth
    println day.dayOfWeek
}

// print out the integer values
todayZoned.with {
    println hourOfDay.value
    println minuteOfHour.value
    println secondOfMinute.value
}

which will produce output like this:

ISO
Year=2008
MonthOfYear=SEPTEMBER
DayOfYear=271
DayOfMonth=27
DayOfWeek=SATURDAY
MonthOfYear=SEPTEMBER
DayOfYear=272
DayOfMonth=28
DayOfWeek=SUNDAY
MonthOfYear=SEPTEMBER
DayOfYear=273
DayOfMonth=29
DayOfWeek=MONDAY
3
46
3

Time Periods

You can also use time periods:

import static javax.time.period.Period.*
def lastNewYears = LocalDateTime.dateTime(2008, 1, 1, 0, 0, 0)
println lastNewYears
def nextNewYears = lastNewYears + years(1)
println nextNewYears

which will produce output like this:

2008-01-01T00:00
2009-01-01T00:00

Input and Output

You can also input or output dates and times using various formatting helper classes including a powerful format building class or using Groovy's GStrings:

// pattern formatting not finalised for JSR-310 at time of writing
// so kludge this using normal Java facilities for now
def input = "20080125"
def fmt_in = new java.text.SimpleDateFormat("yyyyMMdd")
def cal_in = Calendar.instance
cal_in.setTime(fmt_in.parse(input))
def date_in = cal_in.toLocalDate()

// now output using predefined formatter
import javax.time.calendar.format.*
def fmt_out = DateTimeFormatters.isoDate()
println fmt_out.print(date_in)

// now try the format builder
import static javax.time.calendar.ISOChronology.*
import static javax.time.calendar.format.DateTimeFormatterBuilder.TextStyle.*
def fmt = new DateTimeFormatterBuilder().
    appendValue(dayOfMonthRule(), 2).
    appendLiteral('-').
    appendText(monthOfYearRule(), SHORT).
    appendLiteral('-').
    appendText(yearRule()).
    toFormatter()
println fmt.print(today)

fmt = new DateTimeFormatterBuilder().
    appendLiteral('Some ways to represent the current month: ').
    appendValue(monthOfYearRule()).
    appendLiteral(' ').
    appendValue(monthOfYearRule(), 2).
    appendLiteral(' ').
    appendText(monthOfYearRule(), NARROW).
    appendLiteral(' ').
    appendText(monthOfYearRule(), SHORT).
    appendLiteral(' ').
    appendText(monthOfYearRule(), FULL).
    toFormatter()
println fmt.print(today)

// now just using a plain GString
println "$today.dayOfMonth.value-${today.monthOfYear.value.toString().padLeft(2, '0')}-$today.year.value"

which will produce output like this:

2008-01-25
28-Sep-2008
Some ways to represent the current month: 9 09 9 Sep September
28-09-2008

If you want to make things look a little more compact, you can apply a little bit of metaprogramming to overload some of the format builder methods like follows:

DateTimeFormatterBuilder.metaClass.leftShift = { String s -> delegate.appendLiteral s }
DateTimeFormatterBuilder.metaClass.leftShift = { DateTimeFieldRule r -> delegate.appendText r }

Now we can use a simpler builder syntax:

def builder = new DateTimeFormatterBuilder() << dayOfMonthRule() << "/" << monthOfYearRule() << "/" << yearRule()
println builder.toFormatter().print(today)

which will produce output like this:

28/September/2008

Further Information

Copyright (c) 2008 Paul King. All rights reserved.