VASmalltalk – ICU, DateAndTime

When working with Dates and Times, you will not only work with the classes Date and Time – but also with DataAndTime, which combines both classes.

This class is available in most (all) Smalltalks, but the implementations are pretty different – though there is a good public interface common to all Smalltalk implementations.

Pharo stores the date as julian date and the time as seconds since midnight. VASmalltalk stores the value as milliseconds since 1.1.1901 00:00:00.

Instances of this class can be created like:

DateAndTime year: 1901 month: 1 day: 1 hour: 0 minute: 0 second: 0

VASmalltalk converts this – more or less – in UTC and stores the milliseconds value into its instance variable – and remember its offset from UTC (as an instance of Duration) in another instance variable.

For more than four years it is known, that VA does not calculate the offset to UTC during the summer time period in a correct way. I’ve been published several ways under WIndows and now under Linux to get rid of this bug – but who knows how long it takes to get rid of this problem. Please remember this bug – because I mainly used the fixed offset method – therefore the values printed below may be different from thos in your image:

Ok, but now back to the instance creation method above. What do I expect from than method ? Perhaps two answers seem to be suitable here:

* I expect a DateAndTime representing the local time for those values

* I expect a DateAndTime representing the UTC time for those values

Most implementation return a local time oriented value. Both Pharo and VASmalltalk have some problems here – they take the actual offset into account – when the statement is executed. That means:

DateAndTime year: 1901 month: 1 day: 1 hour: 0 minute: 0 second: 0

executed (here in Germany) in (e.g.) January is more or less interpreted as:

31.12.1900 23:00:00 +00:00 or: 01.01.1901 00:00:00 +01:00

if you execute the code in (e.g.) June here in Germany you will get (with the fix):

31.12.1900 22:00:00 +00:00 or: 01.01.1901 00:00:00 +02:00

or without the fix:

31.12.1900 23:00:00 +00:00 or: 01.01.1901 00:00:00 +01:00

and in the last cast this is actually right (by random), because 1901 there was no daylight saving active …

As long as you stay within the DateAndTime instance you have no problems, but when doing printing or serializing (like for example for XML or JSON) you will export the bug and the importer of your time values (the potential receiver of your printed values) get actually wrong results – and even when doing #asMilliseconds you get a wrong UTC value – under circumstances.

Therefore to get rid of the problems above you should always execute the instance creation method including the offset: parameter like:

DateAndTime year: 1901 month: 1 day: 1 hour: 0 minute: 0 second: 0 offset: (Duration hours: 1)

The main problem here is, that the correct offset is perhaps not known for that specific date or at least not easily to calculate (of course 1901 is not a good example – just take later years, when daylight saving was introduced).

But knowing the internal representation – you can understand, why #asUTC is very simple within VASmalltalk. It just sets the offset to a zero Duration, but when the instance variable has the wrong content … then the UTC value is also not correct.

When considering ICU usage one may write:

aDateAndTime:= DateAndTime icuLocalYear:  1901 month: 1 day: 1 hour: 0 minute: 0 second: 0 	

and you get

31.12.1900 23:00:00 +00:00 or: 01.01.1901 00:00:00 +01:00.

And getting into the summer of that year you may execute

aDateAndTime:= DateAndTime icuLocalYear:  1901 month: 6 day: 1 hour: 0 minute: 0 second: 0 	

and you still get: 01.06.1901 00:00:00 +01:00

Then we go to the nearer history (1980)

aDateAndTime:= DateAndTime icuLocalYear:  1980 month: 6 day: 1 hour: 0 minute: 0 second: 0 	

and now times are changing: 01.06.1980 00:00:00 +02:00.

With release 0.0.10 of my wrapper additional code for printing and parsing has been wrapped:

"with 'de_DE' and 'Europe/Berlin'"
aCalendar := UCalendar openDefaultWithDefaultLocaleAndTimezone
anUDateFormat := UDateFormat 
                              openTimeStyle: UDAT_LONG
                              dateStyle: UDAT_LONG 
                              locale: 'en_US' 
                              zoneName: 'UTC' 
                              pattern: nil.
aString := anUDateFormat format: aCalendar millis fieldPosition: nil.

gives:

"May 27, 2011 3:03:31 PM GMT"

or with locale = “de_DE” one gets:

"27. Mai 2011 15:03:31 GMT"

and in addition with zoneName = ‘Europe/Berlin’ one gets:

"27. Mai 2011 17:03:31 MESZ"

With 0.0.10 I added simple support to parse the string back into a DateAndTime instances:

| locale timezone aCalendar anUDateFormat aString aValue |
aCalendar := UCalendar openDefaultWithDefaultLocaleAndTimezone.
anUDateFormat := UDateFormat 
                                openTimeStyle: UDAT_LONG
                                dateStyle: UDAT_LONG 
                                locale: 'de_DE' 
                                zoneName: 'UTC' 
                                pattern: nil.
aValue := anUDateFormat 
                 parse: '27. Mai 2011 17:03:31 MESZ'  
                 calendar: aCalendar 
                 parsePosition: nil.
anUDateFormat close.
aCalendar asDateAndTime inspect

and you get:

"2011-05-27T17:03:31+02:00"
This entry was posted in Smalltalk and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s