Tuesday, March 31, 2015

What time is it? (time zones, ISO 8601 etc.)

Time can be a somewhat complicated thing to deal with, especially if you have to take into account different time zones and daylight saving times.

When dealing with time zones, a good starting point is UTC, a successor to Greenwich Mean Time (GMT).
Time zones are expressed using offsets from UTC. It is good to note that UTC does not observe Daylight saving time. As an example, Finland is normally (at “winter time”) UTC+2 and at the summer with daylight saving time UTC+3.

To present date and time data, ISO 8601 standard is usually the way to go. It supports presenting combined date and time combined with time zone.

Some examples

An example moment in Finland’s time zone during “winter time” (offset +2): 26.3.2015 15:00

  • is presented with offset as: 2015-03-26T15:00:00+02:00
  • is presented in UTC without offset as: 2015-03-26T13:00:00+00:00
  • can be presented with UTC with Z: 2015-03-26T13:00:00Z

An example during daylight saving time (“summer time”, offset +3): 5.4.2015 15:00

  • is 2015-04-05T15:00:00:00+03:00
  • and same as 2015-04-05T12:00:00+00:00
  • and same as 2015-04-05T12:00:00Z

Code examples

When coding, it is often good to present times internally with ISO 8601. In the UI, time should usually be shown in user’s local time zone.

Some questions arise:

  • How to handle the time zone offsets?
  • How to find out what is the time zone offset for a given city in a given time (note that because of DST, the same city often has different offset in different parts of the year)

Examples in JavaScript

Moment.js is a good JavaScript library for handling dates and times. It has extension Moment Timezone to work with time zones.
Moment timezone is in the NPM module moment-timezone.

Case: UTC is known, we want presentation in Helsinki local time:

var moment = require('moment-timezone');
var winterTimeExample = moment("2015-03-25T13:00:00Z");
var summerTimeExample = moment("2015-04-05T12:00:00Z");
winterTimeExample.tz('Europe/Helsinki').format('D.M.YYYY HH.mm');
// -> '25.3.2015 15.00'
summerTimeExample.tz('Europe/Helsinki').format('D.M.YYYY HH.mm');
// -> '5.4.2015 15.00'

Case: Helsinki local time is known, we want UTC:

var moment = require('moment-timezone');
var winterHelsinki  = moment.tz("2015-03-25 15:00", 'Europe/Helsinki');
var summerHelsinki  = moment.tz("2015-04-05 15:00", 'Europe/Helsinki');
winterHelsinki.utc().toISOString() // '2015-03-25T13:00:00.000Z'
summerHelsinki.utc().toISOString() // '2015-04-05T12:00:00.000Z'

Examples in Java

For Java/JVM, Joda-Time is a good library for dates and times. For other JVM languages, there are Joda-Time wrappers, e.g. nscala-time for Scala and clj-time for Clojure.

Case: UTC is known, we want presentation in Helsinki local time:

String winterTimeExample = "2015-03-25T13:00:00Z";
String summerTimeExample = "2015-04-05T12:00:00Z";
        
DateTimeFormatter formatterHelsinki = 
    DateTimeFormat.forPattern("dd.MM.yyyy HH:mm").withZone(DateTimeZone.forID("Europe/Helsinki"));
DateTimeFormatter isoFormatter = 
    ISODateTimeFormat.dateTimeNoMillis().withZoneUTC();

System.out.println(formatterHelsinki.print(isoFormatter.parseDateTime(winterTimeExample)));
// --> 25.03.2015 15:00
System.out.println(formatterHelsinki.print(isoFormatter.parseDateTime(summerTimeExample)));
// --> 05.04.2015 15:00

Case: Helsinki local time is known, we want UTC:

String localWinterTimeExample = "25.03.2015 15:00";
String localSummerTimeExample = "05.04.2015 15:00";
        
DateTimeFormatter formatterHelsinki = 
    DateTimeFormat.forPattern("dd.MM.yyyy HH:mm").withZone(DateTimeZone.forID("Europe/Helsinki"));
DateTimeFormatter isoFormatter = 
    ISODateTimeFormat.dateTimeNoMillis().withZoneUTC();
        
System.out.println(isoFormatter.print(formatterHelsinki.parseDateTime(localWinterTimeExample)));
// --> 2015-03-25T13:00:00Z
System.out.println(isoFormatter.print(formatterHelsinki.parseDateTime(localSummerTimeExample)));
// --> 2015-04-05T12:00:00Z

Offset to UTC might not always be the best approach

After this, check out a good blog post How to save datetimes for future events - related to problems with time zone rules (especially DST) changing. In some cases it might be best to save “wall time” and time zone name for future dates.