Dates and Times

Introduction

Groovy's date and time features build upon Java's. For Java versions through to Java 6, this means working with the following key classes:

Class Description
Date Represents a discrete point in time
Calendar This is the parent class for calendars and has some useful constants and a static factory for creating calendars. Calendars know about fields like month, day, year and time.
DateFormat Used for parsing and generating string versions of dates and times

The date and calendar classes belong to the java.util package which Groovy automatically imports, so you can use them straight away without having to worry about imports.

To these classes, Groovy adds a few enhancements plus a category class for writing pretty time and date DSLs:

Class Description
TimeCategory Allows you to write human friendly date manipulation expressions

Creating date instances

Here is a simple example of using Date:

def today = new Date()
println today

which will produce output like this:

Fri Sep 26 19:23:30 EST 2008

Creating calendar instances

Though we don't normally recommend it (see below), there is a child class of Calendar called GregorianCalendar which you can use directly as follows:

def christmas = new GregorianCalendar(2008, Calendar.DECEMBER, 25)
println christmas.time

which will produce output like this:

Thu Dec 25 00:00:00 EST 2008

You can also combine this with static imports as follows:

import static java.util.Calendar.*
def newyears = new GregorianCalendar(2009, JANUARY, 1)
println newyears.time

which will produce output like this:

Thu Jan 01 00:00:00 EST 2009

But the preferred approach to creating calendars involves calling the getInstance static factory method (shown below using the property access syntax, i.e. instance). This means that the specific details of the calendar system we are using (gregorian for most of us) doesn't clutter up our code. The example below creates a calendar and illustrates how you would set the time zone using short and long time zone values:

newyears = Calendar.instance
newyears.set 2009, JANUARY, 1
newyears.timeZone = TimeZone.getTimeZone("America/Los_Angeles")
newyears.timeZone = TimeZone.getTimeZone("UTC")

Converting between calendar and dates and milliseconds

Calendars can be converted to dates using the getTime method (perhaps it should have been called getDate). And dates can be converted to a long milliseconds value using getTime. The returned value represents the time elapsed in milliseconds since January 1, 1970 00:00:00 GMT. Examples of these methods are shown below:

now = Calendar.instance
println 'now is a ' + now.class.name
date = now.time
println 'date is a ' + date.class.name + ' with value ' + date
millis = date.time
println 'millis is a ' + millis.class.name + ' with value ' + millis

which will produce output like this:

now is a java.util.GregorianCalendar
date is a java.util.Date with value Sat Sep 27 09:36:47 EST 2008
millis is a java.lang.Long with value 1222472207348

Working back the other way is just as easy. Dates have a constructor which takes a long millisecond value and calendars have a setTime method which takes a date:

date = new Date(millis)
now.time = date

Manipulating calendar and dates

Groovy also allows you to perform simple arithmetic with dates as illustrated here:

def newYearsEve = newyears.time - 1
println newYearsEve
def boxingDay = christmas.time + 1
println boxingDay
// or normal Java arithmetic to convert milliseconds to days
def daysBetween = (newYearsEve.time - boxingDay.time) / (24 * 60 * 60 * 1000)
println daysBetween

which will produce output like this:

Wed Dec 31 00:00:00 EST 2008
Fri Dec 26 00:00:00 EST 2008
5

You can also work with get and set operations for calendars or index operations on dates as illustrated here:

def nowCal = Calendar.instance
y = nowCal.get(YEAR)
Date nowDate = nowCal.time
m = nowDate[MONTH] + 1
d = nowDate[DATE]
println "Today is $d $m $y"
nowCal.set DATE, 1
nowCal.set MONTH, FEBRUARY
println "Changed to $nowCal.time"

which will produce output like this:

Today is 26 9 2008
Changed to Fri Feb 01 23:05:20 EST 2008

This mechanism also lets you find out other information about calendars (uses Groovy 1.6 multiple/tuple assignment syntax):

cal = Calendar.instance
cal.set 1988, APRIL, 4, 0, 0, 0
date = cal.time
def (doy, woy, y) = [DAY_OF_YEAR, WEEK_OF_YEAR, YEAR].collect{ date[it] }
println "$date is day $doy and week $woy of year $y"

which will produce output like this:

Mon Apr 04 00:00:00 EST 1988 is day 95 and week 15 of year 1988

Date ranges

You can also work with Groovy ranges:

def holidays = boxingDay..newYearsEve
println holidays.size()
println today in holidays
println holidays.collect{ it.toString()[0] }

which will produce output like this:

6
false
[F, S, S, M, T, W]

Writing dates as strings

You can print dates using DateFormat and SimpleDateFormat:

import java.text.DateFormat
def plainFormatter = DateFormat.instance
println plainFormatter.format(today)

which will produce output like this:

26/09/08 22:07

And combine with locales if needed:

import static java.text.DateFormat.*
import static java.util.Locale.*
[ITALY, FRANCE, GERMAN].each { loc ->
    println getDateInstance(FULL, loc).format(newyears.time)
}

which will produce output like this:

giovedý 1 gennaio 2009
jeudi 1 janvier 2009
Donnerstag, 1. Januar 2009

This example uses SimpleDataFormat in combination with some additional calendar methods for adjusting dates:

import java.text.SimpleDateFormat
printCal = {cal -> new SimpleDateFormat().format(cal.time)}
cal = Calendar.instance
cal.set 2000, JANUARY, 1, 00, 01, 0
assert printCal(cal) == '1/01/00 00:01'
// roll minute back by 2 but don't adjust other fields
cal.roll MINUTE, -2
assert printCal(cal) == '1/01/00 00:59'
// adjust hour back 1 and adjust other fields if needed
cal.add HOUR, -1
assert printCal(cal) == '31/12/99 23:59'

Creating dates from strings

You can input dates using DateFormat or SimpleDateFormat:

Date d1 = getDateTimeInstance(MEDIUM, LONG).parse("25/12/2008 15:33:33")
println d1
Date d2 = new SimpleDateFormat("yyyy-MMM-dd").parse("2008-DEC-25")
println d2

which will produce output like this:

Thu Dec 25 15:33:33 EST 2008
Thu Dec 25 00:00:00 EST 2008

Using Groovy's Time Category

Groovy also has a Time Category class which gives you DSL style syntax for manipulating dates. Here is an example:

import org.codehaus.groovy.runtime.TimeCategory
now = new Date()
println now
use(TimeCategory) {
    footballPractice = now + 1.week - 4.days + 2.hours - 3.seconds
}
println footballPractice

which will produce output like this:

Sat Sep 27 10:00:12 EST 2008
Tue Sep 30 12:00:09 EST 2008

With Groovy 1.6 and above you can also use the mixin notation:

Integer.metaClass.mixin TimeCategory
Date.metaClass.mixin TimeCategory
footballPractice = 1.week.from.now - 4.days + 2.hours - 3.seconds
println footballPractice

Working with Time

We have already know that we can get the current time using a date or calendar. If we need the current time in milliseconds we can get it from the time property. But, you can also access the current time in milliseconds directly:

println System.currentTimeMillis()

which will produce output like this:

1222480762098

This method is convenient if we want to time how long some code might take to execute, e.g.:

// ranges normally unmodifiable, so make a list copy
def reversedNumbers = (10000..1).toList()

// check size and first few elements
println reversedNumbers.size()
println reversedNumbers[0..5]

// now perform the operation we want to time
def before = System.currentTimeMillis()
reversedNumbers.sort()
def after = System.currentTimeMillis()

// check first few elements
println reversedNumbers[0..5]
println "Sorting took ${after-before} ms"

which will produce output like this:

[10000, 9999, 9998, 9997, 9996, 9995]
10000
[1, 2, 3, 4, 5, 6]
Sorting took 16 ms

If you need higher precision than milliseconds, and you have a recent version of Java, you can use:

println System.nanoTime()

which will produce output like this:

828923218382504

If you modifiy the above timing example to use nanotime, your output from running the example would finish with something like:

Sorting took 12255748 ns

Note that nanoTime implementations on some platforms may have limitations. You should check out how it works on your system before relying on its accuracy. On multiprocessor machines, you may find it fixes issues but raises other issues compared with currentTimeMillis. You may also find the call itself has different resource implications compared to currentTimeMillis.

Using Timers

You can also do simple scheduling with Java's Timer classes.

cal = Calendar.instance
cal.roll MINUTE, 1
def timer = new Timer(true)
def task = { println 'Background Task Done' }
timer.schedule task as TimerTask, cal.time

which will produce output like this (about one minute after executing the script):

Background Task Done

Further Information

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