We use class Date for simple date processing:
| Code Block |
|---|
def today= new Date() //represents the date and time when it is created
println today
//we can add to and subtract from a date...
def tomorrow= today + 1,
dayAfter= today + 2,
yesterday= today - 1,
dayBefore= today - 2
println "\n$dayBefore\n$yesterday\n$today\n$tomorrow\n$dayAfter\n"
assert today + 7 == today.plus(7) && today - 15 == today.minus(15)
//equivalent methods
//we can increment and decrement a date...
def d= today.clone()
d++; assert d == tomorrow
d= d.next(); assert d == dayAfter //equivalent method
d--; assert d == tomorrow
d= d.previous(); assert d == today //equivalent method
//we can compare dates...
assert tomorrow.after(today)
assert yesterday.before(today)
assert tomorrow.compareTo(today) > 0
assert tomorrow.compareTo(dayAfter) < 0
assert dayBefore.compareTo(dayBefore) == 0
def n= today.time //we can convert a Date to a Long
println n
today.time = 0 //long 0 is beginning of 1 Jan 1970 GMT
println today
def sometimeAgo= new Date(0) //we can construct a date with a Long argument
assert sometimeAgo == today
|
Other date and time processing can be done using the GregorianCalendar class:
| Code Block |
|---|
System.setProperty('user.timezone', 'GMT') //we'll look at timezones later
def c= new GregorianCalendar()
println c.time //'time' property gives a Date class
c= Calendar.instance
assert c.class == GregorianCalendar //another way to create a GregorianCalendar
println c.time
assert c.timeInMillis == c.time.time
//we can get the time in milliseconds after 1 Jan 1970 at 0:00:00am GMT
println System.currentTimeMillis() //another way to get the current time
println System.nanoTime()
//time in nano-seconds: good for measuring elapsed computation times
c= new GregorianCalendar(2009, Calendar.JULY, 22) //creates 22 July 2009
c= new GregorianCalendar(2009, Calendar.JULY, 22, 2, 35)
//creates 22 July 2009 at 2:35am GMT
c= new GregorianCalendar(2009, Calendar.JULY, 22, 2, 35, 21)
//creates 22 July 2009 at 2:35:21am GMT
c.clear() //if we clear the fields, we get...
assert c.get(Calendar.ERA) == GregorianCalendar.AD &&
c.get(Calendar.YEAR) == 1970 &&
c.get(Calendar.MONTH) == 0 &&
//dates range from 0 to 11, so this is January
c.get(Calendar.WEEK_OF_MONTH) == 1 && //should be: 0
c.get(Calendar.DAY_OF_MONTH) == 1 &&
c.get(Calendar.DATE) == 1 && //same as DAY_OF_MONTH
c.get(Calendar.DAY_OF_WEEK) == 5 &&
c.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 1 &&
c.get(Calendar.AM_PM) == Calendar.AM &&
c.get(Calendar.HOUR) == 0 &&
c.get(Calendar.HOUR_OF_DAY) == 0 &&
c.get(Calendar.MINUTE) == 0 &&
c.get(Calendar.SECOND) == 0 &&
c.get(Calendar.MILLISECOND) == 0 &&
c.get(Calendar.WEEK_OF_YEAR) == 1 &&
c.get(Calendar.DAY_OF_YEAR) == 1
def d= new GregorianCalendar()
d.timeInMillis= 0
//we can set the 'time', here 1 Jan 1970 at 00:00:00.000 GMT (Gregorian)
d.time= new Date(0) //alternative syntax
assert d == c
|
GregorianCalendar supports both the Julian and Gregorian calendar systems, supporting one discontinuity, which by default is when the Gregorian calendar was first instituted in some countries, ie, 4 October 1582 (Julian) followed by 15 October, 1582 (Gregorian). The only difference between the calendars is the leap year rule: the Julian specifies leap years every four years, whereas the Gregorian omits century years which are not divisible by 400. Because dates are computed by extrapolating the current rules indefinitely far backward and forward in time, this calendar generates consistent results for all years, although dates obtained are historically accurate only from March 1, 4 AD onward, when modern Julian calendar rules were adopted. Although New Year's Day was March 25 prior to the institution of the Gregorian calendar, to avoid confusion, this calendar always uses January 1.
From Groovy 1.5.7 / 1.6.x, you may use Date.format() directly. Refer to GROOVY-3066 for details.
Alternatively, Dates and times can be formatted easily with String.format(). The first character is 't' or 'T' for each item:
| Code Block |
|---|
def c1= new GregorianCalendar(1995, Calendar.SEPTEMBER, 5, 19, 35, 30, 750)
//dates...
assert String.format('%tY/%<tm/%<td', c1) == '1995/09/05'
assert String.format('%tA %<te %<tB %<ty', c1) == 'Tuesday 5 September 95'
assert String.format('century:%tC, month:%<tb, day:%<te', c1) ==
'century:19, month:Sep, day:5'
assert String.format('month:%th, day of year:%<tj, day of week:%<ta', c1) ==
'month:Sep, day of year:248, day of week:Tue' //'h' same as 'b'
//times...
assert String.format('%tH:%<tM:%<tS.%<tL', c1) == '19:35:30.750'
assert String.format('%tI%<tp, %<tl%<tp, nanoseconds:%<tN', c1) ==
'07pm, 7pm, nanoseconds:750000000'
assert String.format('%ts', c1) == '810300930'
//seconds since start of 1-Jan-1970 GMT
assert String.format('%tQ', c1) == '810300930750'
//milliseconds since start of 1-Jan-1970 GMT
assert String.format('%tk',
new GregorianCalendar(1995, Calendar.SEPTEMBER, 5, 6, 35)) == '6'
//shortcut formats...
assert String.format('%tF', c1) == '1995-09-05' //date as '%tm/%td/%ty'
assert String.format('%tD', c1) == '09/05/95' //date as '%tY-%tm-%td'
assert String.format('%tT', c1) == '19:35:30' //time as '%tH:%tM:%tS'
assert String.format('%tR', c1) == '19:35' //time as '%tH:%tM'
assert String.format('%tr', c1) == '07:35:30 PM' //time as '%tI:%tM:%tS %Tp'
//additionally...
assert String.format('%tF', new Date(0)) == '1970-01-01'
//we can supply a Date instead of a Calendar
assert String.format('%tF', 0L) == '1970-01-01' //we can also supply a long
assert String.format('...%15tF...', 0L) == '... 1970-01-01...' //width 15
assert String.format('...%-15tF...', 0L) == '...1970-01-01 ...'
// '-' flag to left-justify
|
After setting fields, we must call any get(), add(), or roll() method, or access the 'timeInMillis' or 'time' properties, to cause other relevant fields to update themselves:
| Code Block |
|---|
System.setProperty('user.timezone', 'GMT')
def c= new GregorianCalendar()
c.set( Calendar.ERA, GregorianCalendar.AD )
c.set( Calendar.YEAR, 1949 )
c.set( Calendar.MONTH, Calendar.OCTOBER )
c.set( Calendar.DATE, 31 )
assert String.format('%tF %<ta', c) == '1949-10-31 Mon'
//properties for calculating WEEK_OF_YEAR and WEEK_OF_MONTH fields...
c.firstDayOfWeek = Calendar.SUNDAY //Sunday in most countries, Monday in others
c.minimalDaysInFirstWeek = 1
assert c.get(Calendar.ERA) == GregorianCalendar.AD &&
c.get(Calendar.YEAR) == 1949 &&
c.get(Calendar.MONTH) == 9 && //dates range from 0 to 11, so October
c.get(Calendar.MONTH) == Calendar.OCTOBER && //alternatively
c.get(Calendar.DAY_OF_MONTH) == 31 &&
c.get(Calendar.WEEK_OF_YEAR) == 45 && //range from 1 to 53
c.get(Calendar.WEEK_OF_MONTH) == 6 && //range from 1 to 6
c.get(Calendar.DAY_OF_YEAR) == 304 &&
c.get(Calendar.DAY_OF_WEEK) == 2 && //Monday
c.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 5
//changing the month uses the same year and day of month...
c.set(Calendar.MONTH, Calendar.AUGUST )
c.time //cause other fields to update themselves
assert String.format('%tF %<ta', c) == '1949-08-31 Wed'
c.set(Calendar.MONTH, Calendar.APRIL )
//...but may cause adjustment to roll into following month
c.time
assert String.format('%tF %<ta', c) == '1949-05-01 Sun'
c.set(Calendar.DATE, 31 )
c.set(Calendar.MONTH, Calendar.FEBRUARY )
c.set(Calendar.MONTH, Calendar.SEPTEMBER )
//rolling into following month only occurs when other fields update themselves,
//call this method to trigger it...
c.time
assert String.format('%tF %<ta', c) == '1949-10-01 Sat'
//...so Feb-28 DIDN'T roll into Mar-03
//changing the day of month uses the same month and year...
c.set( Calendar.DATE, 1 ); c.time
assert String.format('%tF %<ta', c) == '1949-10-01 Sat'
//changing the day of year adjusts the month, day, and other date fields...
c.set(Calendar.DAY_OF_YEAR, c.get(Calendar.DAY_OF_YEAR) + 2 ); c.time
assert String.format('%tF %<ta', c) == '1949-10-03 Mon'
//changing the week of year keeps the same day of week, but adjusts
//the other date fields...
c.set(Calendar.WEEK_OF_YEAR, c.get(Calendar.WEEK_OF_YEAR) + 3 ); c.time
assert String.format('%tF %<ta', c) == '1949-10-24 Mon'
//changing the week of month keeps both the same month and day of week...
c.set(Calendar.WEEK_OF_MONTH, c.get(Calendar.WEEK_OF_MONTH) - 2 ); c.time
assert String.format('%tF %<ta', c) == '1949-10-10 Mon'
//changing the day of week in month also keeps both the
//same month and day of week...
c.set(Calendar.DAY_OF_WEEK_IN_MONTH, c.get(Calendar.DAY_OF_WEEK_IN_MONTH) - 1 )
c.time
assert String.format('%tF %<ta', c) == '1949-10-03 Mon'
//changing the day of week keeps the same week in year...
c.set( Calendar.DAY_OF_WEEK, Calendar.SATURDAY ); c.time
assert String.format('%tF %<ta', c) == '1949-10-08 Sat'
c.set( Calendar.DAY_OF_WEEK, Calendar.SUNDAY ); c.time
assert String.format('%tF %<ta', c) == '1949-10-02 Sun'
|
We can also set the time in this way:
| Code Block |
|---|
System.setProperty('user.timezone', 'GMT')
def c= new GregorianCalendar( 1949, Calendar.OCTOBER, 2 )
c.set( Calendar.AM_PM, Calendar.AM )
c.set( Calendar.HOUR, 6 ) //set the AM_PM and HOUR fields...
c.set( Calendar.MINUTE, 30 )
c.set( Calendar.SECOND, 15 ); c.time
assert String.format('%tF %<tT', c) == '1949-10-02 06:30:15'
assert c.get( Calendar.HOUR_OF_DAY ) == 6
//...and the HOUR_OF_DAY field is updated...
c.set( Calendar.HOUR_OF_DAY, 19 ); c.time
assert String.format('%tF %<tT', c) == '1949-10-02 19:30:15'
assert c.get( Calendar.HOUR ) == 7 && c.get( Calendar.AM_PM ) == Calendar.PM
//...and vice versa
c.set( Calendar.AM_PM, Calendar.AM ); c.time
assert String.format('%tF %<tT', c) == '1949-10-02 07:30:15' &&
c.get( Calendar.AM_PM ) == Calendar.AM
c.set( Calendar.HOUR, 18 ); c.time
//if we set the HOUR with a 24-hr value, it self-adjusts
assert c.get( Calendar.HOUR ) == 6 && c.get( Calendar.AM_PM ) == Calendar.PM
//there's no 24:00, only 00:00 which is 'am', on the following day...
c= new GregorianCalendar(1950, Calendar.JANUARY, 26, 23, 59)
assert String.format('%tF %<tT %<tp', c) == '1950-01-26 23:59:00 pm'
c.add( Calendar.MINUTE, 1 )
assert String.format('%tF %<tT %<tp', c) == '1950-01-27 00:00:00 am'
//12:00 noon is 'pm'...
c= new GregorianCalendar(1950, Calendar.JANUARY, 27, 12, 00)
assert String.format('%tF %<tT %<tp', c) == '1950-01-27 12:00:00 pm'
|
More field manipulations:
| Code Block |
|---|
System.setProperty('user.timezone', 'GMT')
//we can set common fields using terser syntax...
def c= new GregorianCalendar()
c.set( 1947, Calendar.AUGUST, 11 ); c.time
assert String.format('%tF %<ta', c) == '1947-08-11 Mon'
c.set( 1947, Calendar.AUGUST, 12, 6, 30 ); c.time
assert String.format('%tF %<ta', c) == '1947-08-12 Tue'
c.set( 1947, Calendar.AUGUST, 15, 6, 30, 45 ); c.time
assert String.format('%tF %<ta', c) == '1947-08-15 Fri'
//we can clear individual fields, and check if they're set...
assert c.isSet( Calendar.YEAR ) && c.isSet( Calendar.MONTH )
c.clear( Calendar.YEAR )
assert ! c.isSet( Calendar.YEAR ) && c.isSet( Calendar.MONTH )
//we can check different maximums and minimums of a field...
c.set( 1947, Calendar.APRIL, 11 ); c.time
assert c.getMinimum( Calendar.DATE ) == 1 &&
c.getMaximum( Calendar.DATE ) == 31
assert c.getActualMinimum( Calendar.DATE ) == 1 &&
c.getActualMaximum( Calendar.DATE ) == 30
assert c.getGreatestMinimum( Calendar.DATE ) == 1 &&
c.getLeastMaximum( Calendar.DATE ) == 28
//the first week in a year may be numbered as part of the previous year,
//and in a month as 0...
c.firstDayOfWeek = Calendar.SUNDAY
c.minimalDaysInFirstWeek = 1
c.set( 1954, Calendar.JANUARY, 1 ); c.time
assert String.format('%tF %<ta', c) == '1954-01-01 Fri'
assert c.get(Calendar.WEEK_OF_YEAR) == 1
assert c.get(Calendar.WEEK_OF_MONTH) == 1
assert c.firstDayOfWeek == Calendar.SUNDAY &&
c.minimalDaysInFirstWeek == 1
c.firstDayOfWeek = Calendar.MONDAY
c.minimalDaysInFirstWeek = 4 //trigger different week numbering
assert c.get(Calendar.WEEK_OF_YEAR) == 53
assert c.get(Calendar.WEEK_OF_MONTH) == 0
c.set( 1956, Calendar.DECEMBER, 31 ); c.time
assert String.format('%tF %<ta', c) == '1956-12-31 Mon'
assert c.get(Calendar.WEEK_OF_YEAR) == 1
//last week of year may be numbered as first of next
|
We can compare dates:
| Code Block |
|---|
c1= new GregorianCalendar(2008, Calendar.AUGUST, 8)
c2= new GregorianCalendar(2009, Calendar.JULY, 22)
assert c1.before( c2 ) && c2.after( c1 )
assert c1.compareTo( c2 ) < 0 &&
c2.compareTo( c1 ) > 0 &&
c1.compareTo( c1 ) == 0
|
As well as using set(), calendar fields can be changed using add() and roll(), both of which force all fields to update themselves:
| Code Block |
|---|
def c= new GregorianCalendar(1999, Calendar.AUGUST, 31)
assert String.format('%tF %<ta', c) == '1999-08-31 Tue'
c.add(Calendar.MONTH, 13)
assert String.format('%tF %<ta', c) == '2000-09-30 Sat'
//we DON'T roll to Oct-01
c= new GregorianCalendar(1999, Calendar.AUGUST, 31)
c.roll(Calendar.MONTH, 13) //rolls a field without changing larger fields
assert String.format('%tF %<ta', c) == '1999-09-30 Thu'
c.roll(Calendar.MONTH, true) //rolls +1
assert String.format('%tF %<ta', c) == '1999-10-30 Sat'
c.roll(Calendar.MONTH, false) //rolls -1
assert String.format('%tF %<ta', c) == '1999-09-30 Thu'
|
We can turn off the lenient mode for field updates to force us to give calendars precisely correct values:
| Code Block |
|---|
System.setProperty('user.timezone', 'GMT')
def c= new GregorianCalendar( 2002, Calendar.JUNE, 30 )
assert c.lenient
c.set( Calendar.DATE, 31 ); c.time
assert String.format('%tF %<ta', c) == '2002-07-01 Mon'
c= new GregorianCalendar( 2002, Calendar.JUNE, 30 )
c.lenient= false
c.set( Calendar.DATE, 31 )
try{ c.time; assert 0 }catch(e){ assert e in IllegalArgumentException }
|
Durations
We can use durations:
| Code Block |
|---|
import groovy.time.*
class Extras{
static toString(BaseDuration it){
def list= []
if(it.years != 0) list<< "$it.years yrs"
if(it.months != 0) list<< "$it.months mths"
if(it.days != 0) list<< "$it.days days"
if(it.hours != 0) list<< "$it.hours hrs"
if(it.minutes != 0) list<< "$it.minutes mins"
if(it.seconds != 0 || it.millis != 0) list<< "$it.seconds.$it.millis secs"
list.join(', ')
}
}
//enable utility methods for duration classes using 'category' syntax,
//introduced in a later tutorial...
use(Extras){
[ {new TimeDuration( 12, 30, 0, 0 )}: '12 hrs, 30 mins',
{new TimeDuration( 4, 12, 30, 0, 0 )}:'4 days, 12 hrs, 30 mins',
{new Duration( 4, 12, 30, 0, 500 )}: '4 days, 12 hrs, 30 mins, 0.500 secs',
{new DatumDependentDuration( 7, 6, 0, 12, 30, 0, 0 )}:
'7 yrs, 6 mths, 12 hrs, 30 mins',
].each{
assert it.key().toString() == it.value
}
}
def convertToMilliseconds= { yr, mth, day, hr, min, sec, mil->
mil + 1000g*( sec + 60g*( min + 60g*( hr + 24g*(
day + 30g*( mth + 12g*yr )
))))
}
assert new TimeDuration( 12, 30, 0, 0 ).toMilliseconds() ==
convertToMilliseconds( 0, 0, 0, 12, 30, 0, 0 )
//ignores 61-second leap minutes
assert new Duration( 114, 12, 30, 0, 0 ).toMilliseconds() ==
convertToMilliseconds( 0, 0, 114, 12, 30, 0, 0 )
//ignores 25-hour daylight-saving days
assert new DatumDependentDuration( 5, 1, 14, 12, 30, 0, 0 ).toMilliseconds() !=
convertToMilliseconds( 5, 1, 14, 12, 30, 0, 0 )
//considers 31-day months and leap-years
|
These durations can be created more easily within the TimeCategory:
| Code Block |
|---|
import groovy.time.*
//reuse Extras category from a previous example...
use( [Extras, groovy.time.TimeCategory] ){
assert 10.years.class == DatumDependentDuration
assert 10.years.toString() ==
new DatumDependentDuration( 10, 0, 0, 0, 0, 0, 0 ).toString()
assert 4.months.toString() ==
new DatumDependentDuration( 0, 4, 0, 0, 0, 0, 0 ).toString()
assert 7.weeks.toString() == new Duration( 49, 0, 0, 0, 0 ).toString()
assert 5.days.toString() == new Duration( 5, 0, 0, 0, 0 ).toString()
assert 12.hours.toString() == new TimeDuration( 12, 0, 0, 0 ).toString()
assert 15.minutes.toString() == new TimeDuration( 0, 15, 0, 0).toString()
assert 13.seconds.toString() == new TimeDuration( 0, 0, 13, 0 ).toString()
assert 750.milliseconds.toString() ==
new TimeDuration( 0, 0, 0, 750 ).toString()
assert 1.day.toString() == new Duration( 1, 0, 0, 0, 0 ).toString()
//we can use the singular name for any of these...
assert 25.minute.toString() == new TimeDuration( 0, 25, 0, 0 ).toString()
//...even when not grammatical in English
}
|
We can add and subtract durations of different types together:
| Code Block |
|---|
import groovy.time.*
//reuse Extras category from a previous example...
use( [Extras, groovy.time.TimeCategory] ){
assert (10.years + 4.months).class == DatumDependentDuration
assert (10.years + 4.months).toString() ==
new DatumDependentDuration( 10, 4, 0, 0, 0, 0, 0 ).toString()
assert (10.years.plus(4.months) ).toString() ==
(10.years + 4.months).toString() //alternative method name
assert (4.months + 10.years).toString() == (10.years + 4.months).toString()
//all duration operations are commutative
assert (10.years + 4.weeks).class == DatumDependentDuration
assert (5.days + 7.weeks).class == Duration
assert (5.days + 17.hours).class == TimeDuration
assert (10.minutes + 5.seconds).class == TimeDuration
//adding a DatumDependentDuration and a TimeDuration gives a
//specially-defined TimeDatumDependentDuration...
assert (10.years + 12.hours).toString() ==
new TimeDatumDependentDuration( 10, 0, 0, 12, 0, 0, 0 ).toString()
assert (10.years + 12.hours).class == TimeDatumDependentDuration
assert ( 10.years + new TimeDatumDependentDuration( 0, 0, 0, 12, 0, 0, 0 )
).class == TimeDatumDependentDuration
assert ( 10.days + new TimeDatumDependentDuration( 0, 0, 0, 12, 0, 0, 0 )
).class == TimeDatumDependentDuration
assert ( 10.minutes + new TimeDatumDependentDuration( 0, 0, 0, 12, 0, 0, 0 )
).class == TimeDatumDependentDuration
assert ( new TimeDatumDependentDuration( 0, 0, 0, 12, 0, 0, 0 ) +
new TimeDatumDependentDuration( 0, 0, 0, 0, 10, 0, 0 )
).class == TimeDatumDependentDuration
//subtracting durations...
assert (10.years - 4.months).class == DatumDependentDuration
assert (10.years - 4.months).toString() ==
new DatumDependentDuration( 10, -4, 0, 0, 0, 0, 0 ).toString()
assert (10.years.minus(4.months) ).toString() ==
(10.years - 4.months).toString() //alternative method name
assert (10.years - 12.hours).class == DatumDependentDuration
assert (5.days - 7.weeks).class == Duration
assert (5.days - 17.hours).class == TimeDuration
assert (10.minutes - 5.seconds).class == TimeDuration
assert (10.years - 4.weeks).class == DatumDependentDuration
}
|
We can add a Date to a duration to give another Date. A TimeDuration takes leap minutes into account, a Duration also takes daylight saving into account, and a DatumDependentDuration considers 31-day months and leap-years:
| Code Block |
|---|
import groovy.time.*
//reuse Extras category from a previous example...
use( [Extras, groovy.time.TimeCategory] ){
def today= new Date(),
tomorrow= today + 1,
dayAfter= today + 2,
nextWeek= today + 7 //days-only Date arithmetic
assert ( today + 7.days ).toString() == nextWeek.toString()
//use Date and duration together
assert ( today.plus(7.days) ).toString() == ( today + 7.days ).toString()
//alternative method name
assert ( 7.days + today ).toString() == nextWeek.toString()
//commutative
assert ( nextWeek - 6.days ).toString() == tomorrow.toString()
assert ( nextWeek.minus(6.days) ).toString() == tomorrow.toString()
//alternative method name
assert ( nextWeek - dayAfter ).toString() == 5.days.toString()
//subtract two dates to get a duration
//some handy operations...
[2.days.ago, 3.days.from.now, 3.days.from.today].each{
assert it.class == java.sql.Date
}
}
|
Time Zones
We can retrieve lists of all time zones on a system:
| Code Block |
|---|
//we can get all available time zone ID's, and get the time zone for an ID...
TimeZone.availableIDs.toList().groupBy{ TimeZone.getTimeZone(it).rawOffset }.
entrySet().sort{it.key}.reverse().each{
println String.format('%6.2f hrs: %2d',
it.key / (60*60*1000), it.value.size())
it.value.each{
def tz= TimeZone.getTimeZone(it)
println "${' '*8}$tz.displayName ($tz.ID): " +
"${tz.DSTSavings / (60*60*1000)}, ${tz.useDaylightTime()}"
}
}
//we can get all the available time zone ID's for a specific offset...
TimeZone.getAvailableIDs( 12 * (60*60*1000) ).toList().each{
def tz= TimeZone.getTimeZone(it)
println "$tz.displayName ($tz.ID): " +
"${tz.DSTSavings / (60*60*1000)}, ${tz.useDaylightTime()}"
}
|
We can access various time zones individually:
| Code Block |
|---|
def tz= TimeZone.'default' //look at the default time zone
println "$tz.displayName ($tz.ID): offset $tz.rawOffset, " +
"dstSaving $tz.DSTSavings, useDST ${tz.useDaylightTime()}"
TimeZone.'default'= TimeZone.getTimeZone('GMT') //set the default time zone
//get a specific time zone from the system...
tz = TimeZone.getTimeZone('America/Los_Angeles')
assert tz.displayName == 'Pacific Standard Time' &&
tz.rawOffset == -8 * (60*60*1000) &&
tz.useDaylightTime() &&
tz.DSTSavings == 1 * (60*60*1000)
//we can fetch a custom time zone, without any daylight saving, by
//supplying a string...
[ 'GMT-8': 'GMT-08:00',
'GMT+11': 'GMT+11:00', //hours must be less than 24
'GMT+0300': 'GMT+03:00',
'GMT-3:15': 'GMT-03:15',
'moo': 'GMT', //syntax errors give GMT
].each{ assert TimeZone.getTimeZone( it.key ).ID == it.value }
|
We can create a time zone with custom daylight-saving time rules:
| Code Block |
|---|
//in the constructor, we can encode the rules for starting or ending
//Daylight Saving time...
def stz= new SimpleTimeZone(-8*(60*60*1000), //base GMT offset: -8:00
"America/Death_Valley",
Calendar.MARCH, 1, 0, //DST starts on 1 March exactly
2*(60*60*1000), SimpleTimeZone.STANDARD_TIME,
//...at 2:00am in standard time (wall time)
Calendar.OCTOBER, 1, -Calendar.SUNDAY,
//ends first Sun on/after 1 Oct (first Sun in Oct)...
2*(60*60*1000), SimpleTimeZone.WALL_TIME,
//...at 2:00am in daylight time (wall time)
1*(60*60*1000) ) // save 1 hour
//leave out last parameter which defaults to 'save 1 hour', ie, 1*(60*60*1000)
stz= new SimpleTimeZone(15*(60*60*1000), //base GMT offset: +15:00
"Pacific/Happy_Isle",
Calendar.AUGUST, -21, -Calendar.FRIDAY,
//starts on last Friday on or before 21 August...
2*(60*60*1000), //...at 2:00am in standard time (wall time, the default)
Calendar.APRIL, 1, -Calendar.SUNDAY,
//ends first Sun on/after 1 Apr (first Sun in Apr)...
2*(60*60*1000) ) //...at 2:00am in daylight time (wall time, the default)
//two extra optional parameters (if present, both must be)...
stz= new SimpleTimeZone( 1*(60*60*1000), //base GMT offset: +1:00
"Europe/Alps",
Calendar.JUNE, 8, -Calendar.MONDAY,
//starts first Mon on/after 8 Jun (second Mon in Jun)...
1*(60*60*1000), SimpleTimeZone.UTC_TIME, //...at 1:00am in UTC time
Calendar.OCTOBER, -1, Calendar.SUNDAY,
//ends on the last Sunday in October...
1*(60*60*1000), SimpleTimeZone.UTC_TIME, //...at 1:00am in UTC time
1*(60*60*1000) ) // save 1 hour
//we can instead encode the rules in the same way using methods...
stz= new SimpleTimeZone( -8*(60*60*1000), //base GMT offset: -8:00
"America/Death_Valley" ) //no daylight-saving schedule in constructor
stz.setStartRule(Calendar.APRIL, 1, -Calendar.SUNDAY, 2 * 60*60*1000)
//first Sun in Apr
stz.setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2 * 60*60*1000)
//last Sun in Oct
assert stz.dSTSavings == 60*60*1000 //the default
stz.dSTSavings= 2 * 60*60*1000
assert stz.dSTSavings == 2 * 60*60*1000
assert stz.getDSTSavings() == 2 * 60*60*1000
//unusually-cased property name 'dSTSavings' has equivalent method names
//'getDSTSavings()' and 'setDSTSavings()'
stz.setStartRule(Calendar.MAY, 1, 2 * 60*60*1000)
//shortcut method for fixed date in month
stz.setStartRule(Calendar.MAY, 10, Calendar.SUNDAY, 2 * 60*60*1000, true)
//shortcut for first Sunday on or after 10 May; true means 'after'
stz.setEndRule(Calendar.OCTOBER, 20, Calendar.SATURDAY, 2 * 60*60*1000, false)
//shortcut for first Saturday on or before 20 October; false means 'before'
|
(Coordinated universal time, UTC, being based on an atomic clock, enables an extra second, a "leap second", to be added as the last second of the day on December 31 or June 30.)
We can use time zones in many various ways:
| Code Block |
|---|
System.setProperty('user.timezone', 'GMT') //we can set the default time zone
def tz= new SimpleTimeZone( -8*(60*60*1000), 'Somewhere',
Calendar.MARCH, 1, 0, 2*(60*60*1000),
Calendar.OCTOBER, 31, 0, 2*(60*60*1000) )
def cal= new GregorianCalendar( tz )
//create a calendar with today's date in a specified time zone
cal= Calendar.getInstance( tz ) //another way
cal= new GregorianCalendar(2009, Calendar.JULY, 22)
//we can create a calendar with the default time zone...
cal.timeZone= tz //...then set the time zone
assert cal.timeZone == tz
assert cal.get(Calendar.ZONE_OFFSET) == -8*(60*60*1000)
assert cal.get(Calendar.DST_OFFSET) == (60*60*1000)
assert Calendar.FIELD_COUNT == 17
//the number of fields such as DAY_OF_YEAR and ZONE_OFFSET in Calendar
//we can test whether two time zones have the same rules...
assert tz.hasSameRules(
new SimpleTimeZone( -8*(60*60*1000), 'Somewhere Else',
Calendar.MARCH, 1, 0, 2*(60*60*1000),
Calendar.OCTOBER, 31, 0, 2*(60*60*1000)
) )
assert ! tz.hasSameRules(
new SimpleTimeZone( -8*(60*60*1000), 'Somewhere Else',
Calendar.APRIL, 1, 0, 2*(60*60*1000),
Calendar.OCTOBER, 31, 0, 2*(60*60*1000)
) )
//some methods available within TimeCategory...
use(groovy.time.TimeCategory){
cal= new GregorianCalendar( tz )
def today= cal.time
println today.timeZone
println today.daylightSavingsOffset //returns a duration
def nextWeek= today + 7
println( (nextWeek - today).daylightSavingsOffset )
//a duration also has a daylight savings time offset
println( nextWeek.getRelativeDaylightSavingsOffset( today ) )
}
//we can test if a certain date is in daylight saving time for a time zone...
assert tz.inDaylightTime( new GregorianCalendar(1990, Calendar.MAY, 5).time )
assert ! tz.inDaylightTime(
new GregorianCalendar(1990, Calendar.NOVEMBER, 5).time )
//we can set the first year daylight savings time operates...
tz.startYear= 1973
assert ! tz.inDaylightTime( new GregorianCalendar(1971, Calendar.MAY, 5).time )
//some extra format codes for dates...
println String.format('%tZ', cal)
//to see a string representing the time zone, eg, GMT-07:00
println String.format('%tz', cal) //numeric offset from GMT, eg, -0800
assert String.format('%tc', cal) ==
String.format('%ta %<tb %<td %<tT %<tZ %<tY', cal)
//we can view the Gregorian changeover date...
assert String.format( '%ta %<td %<tb %<tY', cal.gregorianChange ) ==
'Fri 15 Oct 1582' //default for GMT time zone
cal= new GregorianCalendar()
cal.set(1582, Calendar.OCTOBER, 15)
cal.time
assert String.format( '%ta %<td %<tb %<tY', cal.time - 1 ) ==
'Thu 04 Oct 1582' //the day before the big change
//check for leap years (this instance method acts like a static method)...
[1999, 1998, 1997, 1900, 1800, 1700].each{ assert ! cal.isLeapYear(it) }
[2000, 1996, 1992, 1600, 1500, 1400].each{ assert cal.isLeapYear(it) }
//1500 and before use Julian calendar rules
|