1

I'm working on a critical application which cares about daylight saving time change.
I'm trying to simulate manually what could happen at runtime by comparing thow date which cross the daylight saving change, so I made the below test. My current location is Italy so the change from CEST (Central European Summer Time) to CET (Central European Time) this year happens on 25/10.
I used the full time zone names, my timezone is Europe/Rome.

And here it is the test I did:

Calendar before = Calendar.getInstance(TimeZone.getTimeZone("Europe/Rome DST")); //CEST
before.set(Calendar.DAY_OF_MONTH, 25);
before.set(Calendar.MONTH, Calendar.OCTOBER);
before.set(Calendar.HOUR_OF_DAY, 2);
before.set(Calendar.MINUTE, 30);
before.set(Calendar.SECOND, 0);
before.set(Calendar.MILLISECOND, 0);

System.out.println(before.getTime());

Calendar after = Calendar.getInstance(TimeZone.getTimeZone("Europe/Rome")); //CET
after.set(Calendar.DAY_OF_MONTH, 25);
after.set(Calendar.MONTH, Calendar.OCTOBER);
after.set(Calendar.HOUR_OF_DAY, 2);
after.set(Calendar.MINUTE, 30);
after.set(Calendar.SECOND, 0);
after.set(Calendar.MILLISECOND, 0);

System.out.println(after.getTime());

System.out.println(before.compareTo(after));

The output is:

BEFORE DST CHANGE: Sun Oct 25 03:30:00 CET 2015
AFTER DST CHANGE: Sun Oct 25 02:30:00 CET 2015
before.compareTo(after): 1

The comparison result is wrong, i.e. 2:30 CEST is after 2:30 CET, but its the opposite.

I don't know if it's a real test.
Is it Is there any way to fix this?
I tried also with joda time but the result is the same.
Thanks in advance.

landal79
  • 1,431
  • 1
  • 11
  • 22
  • Have a look at the answer to [this question.](http://stackoverflow.com/questions/10545960/how-to-tackle-daylight-savings-using-timezone-in-java) It's possible it would be more appropriate to get your timezones based on location rather than with the abbreviations. – augray May 25 '15 at 16:15
  • @augray, I tried also with tz database time zones IDs, but the result is the same. – landal79 May 25 '15 at 16:33
  • FYI Your update has a typo on this line `before.setTimeZone(TimeZone.getTimeZone(""Europe/Rome DST")"));` – augray May 25 '15 at 16:48
  • @augray thx, I corrected the typo – landal79 May 26 '15 at 07:36

4 Answers4

3

Your problem is that "Europe/Rome DST" is not recognized by getTimeZone(timeZoneId).
When it doesn't understand your input, it returns the GMT timezone by default. You can see the list of available TimeZone ids with getAvailableIDs (the method below getTimeZone at the above link).

It should be noted that CEST is also not on the list. To simulate the CEST timezone you could choose one of the following solutions:

  • I would recommend using TimeZone.setRawOffset(int offsetInMs) to set the offests for CET and CEST yourself.
  • Use one of the timzones that is defined relative to GMT (ex, with id "Etc/GMT+1"). This will ensure that you are using valid timezone offsets the the TimeZone api will understand.
  • Set the DST offset on the calendar instance Calendar.DST_OFFSET.

By using the last solution the correct test code is:

Calendar before = Calendar.getInstance(TimeZone.getTimeZone("Europe/Rome"));
before.set(Calendar.DST_OFFSET, 3600000);       
before.set(Calendar.DAY_OF_MONTH, 25);
before.set(Calendar.MONTH, Calendar.OCTOBER);
before.set(Calendar.HOUR_OF_DAY, 2);
before.set(Calendar.MINUTE, 30);
before.set(Calendar.SECOND, 0);
before.set(Calendar.MILLISECOND, 0);

System.out.println("BEFORE DST CHANGE: " + before.getTime());       

Calendar after = Calendar.getInstance(TimeZone.getTimeZone("Europe/Rome"));
after.set(Calendar.DAY_OF_MONTH, 25);
after.set(Calendar.MONTH, Calendar.OCTOBER);
after.set(Calendar.HOUR_OF_DAY, 2);
after.set(Calendar.MINUTE, 30);
after.set(Calendar.SECOND, 0);
after.set(Calendar.MILLISECOND, 0);

System.out.println("AFTER DST CHANGE: " + after.getTime());

System.out.println("before.compareTo(after): " + before.compareTo(after));

And the output:

BEFORE DST CHANGE: Sun Oct 25 02:30:00 CEST 2015
AFTER DST CHANGE: Sun Oct 25 02:30:00 CET 2015
before.compareTo(after): -1
landal79
  • 1,431
  • 1
  • 11
  • 22
augray
  • 2,685
  • 1
  • 13
  • 26
2

The answer by augray is correct and should be accepted (click that large empty check mark icon to make it turn green). I'll add some thoughts and code.

Use A Good Date-Time Library

Avoid the mess that is the java.util.Date/.Calendard classes. They are notoriously troublesome, flawed in both design and implementation.

These classes have been supplanted by the new java.time package in Java 8 and later (Tutorial). That package was inspired by the Joda-Time library. While similar, java.time and Joda-Time are not identical. Each has features the other lacks. You can use either or both.

Avoid 3-4 Letter Time Zone Codes

Codes such as CET & CEST are neither standardized nor unique. Avoid them.

Use full time zone names. Most of these are "continent/city" or "continent/region".

You seem to be using this codes in an effort to manage the problem of DST, Daylight Saving Time. Leave such heavy-lifting to the date-time library, such as java.time or Joda-Time. A time zone combines an offset from UTC with the set of past, present, and future rules for DST and other anomalies. So you specify the time zone name and let the library do the work of figuring out when DST is in effect.

DST

Daylight Saving Time (DST) for Rome ends this year on October 25, 2015 at 3 AM. Clocks are rolled back to repeat the 2 AM hour. So there are two 2:30 times that day. You can see both of those 2:30 times in example code below.

  • 2015-10-25T02:30+02:00[Europe/Rome]
  • 2015-10-25T02:30+01:00[Europe/Rome]

Example Code

Here is some example code in java.time of Java 8 to see how DST is handled. First we take some "local" date-time, where "local" means without any particular time zone attached. We then assign the Rome time zone. After that we take the zoned values (the Rome values) and either add or subtract hours.

Ambiguity

Note how the notion of "2:30 AM in Rome" is meaningless on the 25th of October. You must know the offset of 01:00 or 02:00 to correctly interpret.

ZoneId zone = ZoneId.of( "Europe/Rome" );

System.out.println( "-----|  Local  |-----------------------------------------\n" );

LocalDateTime local_0130 = LocalDateTime.of( 2015 , Month.OCTOBER , 25 , 1 , 30 );
ZonedDateTime zoned_0130 = ZonedDateTime.of( local_0130 , zone );

LocalDateTime local_0230 = LocalDateTime.of( 2015 , Month.OCTOBER , 25 , 2 , 30 );
ZonedDateTime zoned_0230 = ZonedDateTime.of( local_0230 , zone );

LocalDateTime local_0330 = LocalDateTime.of( 2015 , Month.OCTOBER , 25 , 3 , 30 );
ZonedDateTime zoned_0330 = ZonedDateTime.of( local_0330 , zone );

System.out.println( "local_0130: " + local_0130 + " in zone: " + zone + " is " + zoned_0130 );
System.out.println( "local_0230: " + local_0230 + " in zone: " + zone + " is " + zoned_0230 );
System.out.println( "local_0330: " + local_0330 + " in zone: " + zone + " is " + zoned_0330 + "\n" );

System.out.println( "-----|  Add Hours  |-----------------------------------------\n" );

ZonedDateTime zoned_0130_plus_1H = zoned_0130.plusHours( 1 );
ZonedDateTime zoned_0130_plus_2H = zoned_0130.plusHours( 2 );

System.out.println( "zoned_0130_plus_1H: " + zoned_0130_plus_1H );
System.out.println( "zoned_0130_plus_2H: " + zoned_0130_plus_2H + "\n" );

System.out.println( "-----|  Subtract Hours  |-----------------------------------------\n" );

ZonedDateTime zoned_0330_minus_1H = zoned_0330.minusHours( 1 );
ZonedDateTime zoned_0330_minus_2H = zoned_0330.minusHours( 2 );

System.out.println( "zoned_0330_minus_1H: " + zoned_0330_minus_1H );
System.out.println( "zoned_0330_minus_2H: " + zoned_0330_minus_2H + "\n" );

When run.

-----|  Local  |-----------------------------------------

local_0130: 2015-10-25T01:30 in zone: Europe/Rome is 2015-10-25T01:30+02:00[Europe/Rome]
local_0230: 2015-10-25T02:30 in zone: Europe/Rome is 2015-10-25T02:30+02:00[Europe/Rome]
local_0330: 2015-10-25T03:30 in zone: Europe/Rome is 2015-10-25T03:30+01:00[Europe/Rome]

-----|  Add Hours  |-----------------------------------------

zoned_0130_plus_1H: 2015-10-25T02:30+02:00[Europe/Rome]
zoned_0130_plus_2H: 2015-10-25T02:30+01:00[Europe/Rome]

-----|  Subtract Hours  |-----------------------------------------

zoned_0330_minus_1H: 2015-10-25T02:30+01:00[Europe/Rome]
zoned_0330_minus_2H: 2015-10-25T02:30+02:00[Europe/Rome]
Community
  • 1
  • 1
Basil Bourque
  • 218,480
  • 72
  • 657
  • 915
0

My advice is to use the full identifiers from the IANA time zone database. In your case you should be using "Europe/Rome":

before.setTimeZone(TimeZone.getTimeZone("Europe/Rome"));

This will make sure that proper time zone info is used for the region, including when daylight saving mode needs to be triggered, and possible regional exceptions.

Do not add DST.

YoYo
  • 7,796
  • 8
  • 50
  • 67
0

"Europe/Rome" is not CET, and "Europe/Rome DST" is not CEST.

"Europe/Rome" is the timezone of Italy. It includes not only DST changes, but any other change to the offset that Rome/Italy has had. So "Europe/Rome" is CET when Rome is in CET and it's CEST when Rome is on CET. The whole point of timezones like "Europe/Rome" is that you don't have to care about daylight savings, the timezone will handle it for you.

Lennart Regebro
  • 147,792
  • 40
  • 207
  • 241