Question

I have been googling around this issue, but could not find clear and definitive documentation.

Assuming I have a UTC time and a TimeZone ID, how do I calculate wall time (= UTC time + Timezone offset + daylight savings) in Java knowing that daylight savings change during the year?

I am looking for a tested code example. Thanks.

Was it helpful?

Solution

When you say that you have time in UTC I assume you hold it in Calendar (Date has no notion of time zone, despite the misleading toString()). If you have time e.g. in String you can easily parse it or calendar instance like here:

Calendar summer = new GregorianCalendar(DateUtils.UTC_TIME_ZONE);
summer.set(2011, Calendar.JUNE, 27, 9, 0, 0);

summer represents 9:00 AM UTC on 27th of June 2011. Now all you need to do is to change the time zone from UTC to Melbounre, Australia:

summer.setTimeZone(TimeZone.getTimeZone("Australia/Melbourne"));

I will use FastDateFormat to print the date correctly:

final FastDateFormat formatter = FastDateFormat.getDateTimeInstance(FastDateFormat.SHORT, FastDateFormat.SHORT);

System.out.println(formatter.format(summer));

The time in Melbourne is 19:00 (+10 hours). But change the date to winter:

Calendar winter = new GregorianCalendar(DateUtils.UTC_TIME_ZONE);
winter.set(2011, Calendar.DECEMBER, 27, 9, 0, 0);
System.out.println(formatter.format(winter));

And suddenly the the time in Melbourne is 20:00 (+11 hours).

The difference proves that changing the time zone on Calendar takes DST into account. During June in UTC time zone there is winter in Australia, hence they do not observe DST.

But during winter in UTC there is summer in Australia - and they switch to DST by shifting the clocks by one hour. This is the reason why during winter the difference is +11 hours as opposed to +10 during summer UTC.


But wait! It gets even more interesting when multiple time zones observing DST are taken into account. First I create the same date in Europe/Oslo time zone:

Calendar winter = new GregorianCalendar(TimeZone.getTimeZone("Europe/Oslo"));
winter.set(2011, Calendar.DECEMBER, 27, 9, 0, 0);

9:00 in Oslo during winter is 8:00 UTC but 19:00 in Melbourne (+10 hours).

But the same time in summer:

Calendar summer = new GregorianCalendar(TimeZone.getTimeZone("Europe/Oslo"));
summer.set(2011, Calendar.JUNE, 27, 9, 0, 0);

Is actually 7:00 UTC and 17:00 in Melbourne! +8 hours!

Somehow people assume that the difference between two time zones is always constant ("the difference between Oslo and Melbourne is always 10 hours) - which is not true, especially when different hemispheres are taken into account.

In reality during winter in Oslo (no DST, UTC+1) DST is observed in Melbourne (UTC+11). On the other hand while there is summer in Oslo and DST is observed (UTC+2), it is not observed in Melbourne (UTC+10). Now it becomes obvious why the difference varies between 8 and 10 hours depending on the day of year.

Also remember that the first and last day of DST is not global but chosen arbitrarily for each time zone. This means that the difference of 9 hours is also possible (!) E.g. check out the 1st of April this year.

OTHER TIPS

When you use Date in java it's always in UTC internally. And Timezone include DST settings so it's actually quite easy.

public static void main(String[] args) throws ParseException {
    String stringAugust = "2011-08-01 12:00:00";
    String stringNovember = "2011-11-01 12:00:00";

    // Outputting the time in Stockholm and Santiago
    // Stockholm has DST in August and not in November
    // Santiago has DST in November and not in August

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    // Parsing the Strings
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date dateAugust = sdf.parse(stringAugust);
    Date dateNovember = sdf.parse(stringNovember);

    // outputting the dates for Stockholm
    sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
    sdf.setTimeZone(TimeZone.getTimeZone("Europe/Stockholm"));
    System.out.println(sdf.format(dateAugust));
    System.out.println(sdf.format(dateNovember));

    // outputting the dates for Santiago
    sdf.setTimeZone(TimeZone.getTimeZone("America/Santiago"));
    System.out.println(sdf.format(dateAugust));
    System.out.println(sdf.format(dateNovember));

}

outputs

2011-08-01 14:00:00 +0200
2011-11-01 13:00:00 +0100
2011-08-01 08:00:00 -0400
2011-11-01 09:00:00 -0300
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top