turrutia turrutia - 6 months ago 13
Java Question

Java Calendar returns wrong hour in MS Windows for `America/Santiago` zone

I have a desktop application (That must run in Windows) that needs to log a timestamp when a specific action is performed, but the time I get when the calendar is called comes with the wrong hour and the correct timezone.
Running some tests I discovered if I uncheck the "Automatically Adjust clock for Daylight Savings Time" checkbox on the windows Timezone settings the calendar provides the correct hour. This problem is not present on Linux. Unfortunately I'm not allowed to make any changes on the windows machine.

More info:


  • Timezone set in windows clock: "America/Santiago" (-4:00).

  • Test Code:

    public class Test2 {

    public static void main(String[] args) {
    SimpleDateFormat sdfBase = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
    long milisBase = Calendar.getInstance().getTimeInMillis();
    Calendar calenBase = Calendar.getInstance();
    Date fechaBase = calenBase.getTime();
    String fechaStringBase = sdfBase.format(fechaBase);
    TimeZone tzBase = Calendar.getInstance().getTimeZone();


    System.out.println("Time (in mill): " + milisBase);
    System.out.println("TimeZone: " + tzBase.getDisplayName() + " - " + tzBase.getID() + " -Offset: " + tzBase.getRawOffset());
    System.out.println("Date Calendar: " + fechaBase);
    System.out.println("Fecha StringBase: " + fechaStringBase);
    }
    }



Obtaining the following results:

With "Automatically Adjust clock for Daylight Savings Time" Checked (Windows clock: 17:50:07)

Time (in mill): 1464645007120
TimeZone: Chile Time - America/Santiago -Offset: -10800000
Date Calendar: Mon May 30 18:50:07 CLT 2016
Fecha StringBase: 30-05-2016 18:50:07


With "Automatically Adjust clock for Daylight Savings Time" Unchecked (Windows clock: 17:50:37)

Time (in mill): 1464645037914
TimeZone: GMT-04:00 - GMT-04:00 -Offset: -14400000
Date Calendar: Mon May 30 17:50:37 GMT-04:00 2016
Fecha StringBase: 30-05-2016 17:50:37


Example: The action is performed when the windows clock reads 13:00:00 (America/Santiago -4:00), but the timestamp marks the time at 14:00:00(America/Santiago -3:00).I uncheck the windows "Automatically Adjust clock for Daylight Savings Time" and since its not DST the windows clock remains the same, I perform the action again and the timestamp now reads the same hour that the windows clock.

I have found prevous questions about the timezone and DST changes in that timezone,but most of them refer to the 2015 issue where Chile didnt change to DST.

Any way to tackle this without intervening the windows machine?

Answer

java.time

You are using the old date-time classes bundled with early Java that have proven to be troublesome. In Java 8 and later those classes were supplanted by the java.time framework (inspired by the Joda-Time library). Avoid the old classes.

Focus on UTC

Programmers should think first in UTC. Always verify the current time in UTC to be sure the computer even has the correct time. Flipping around time zones and Daylight Saving Time (DST) changes will make your brain hurt, and UTC is your aspirin.

In Java 8 and later, the Instant class represents a moment on the timeline in UTC with a resolution in nanoseconds.

Instant now = Instant.now();
System.out.println( "Instant.now(): " + now );

Always include the UTC value in a post such as this Question.

Time zone of host OS does not matter

The JVM holds it own representation of its current default time zone. By default in most implementations that zone is picked up from the host OS’ time zone setting. But Java launch configurations can override that default. And any Java code in any thread of any app within the JVM can change the JVM’s current default time zone during runtime with a call to TimeZone.setDefault.

So the current time zone setting of the host OS is irrelevant. What matters is the current setting in your JVM.

The part of your host OS that does matter is if its hardware clock knows the current moment correctly. In most JVM implementations, the current moment is determined from the host hardware clock, and then the JVM applies its own current default time zone.

Time Zone redefined for Chile in early 2016

Politicians enjoy fiddling with time zones and Daylight Saving Time (DST). Chile is no exception. See the Wikipedia article, Time in Chile.

From what I could discern… Last year, 2015, Chile stayed on DST permanently at -03:00 and deciding to never revert back to the standard time of -04:00. Then they changed their minds again, announcing in March 2016 that they would re-introduce standard time for their winter (southern hemisphere, winter being now, late May, June, July). On 2016-05-15 midnight (a couple weeks ago), the old standard offset of -04:00 kicked in. That standard/winter time will endure until 2016-08-14 (or until they change their minds again). [Take all that with a grain of salt. Please verify that I got the facts correct.]

These frequent changes wreak havoc with computers which need to have their tz time zone database files updated in order to reflect the latest fiddling.

Your operating system has its own copy of the time zone definitions. Java has its own copy. And other software such as your database engine may have its own copy.

Current Java 8 Update 92 lacks recent Chile changes

So let's run a test in Java 8 Update 92 to see if the Java copy of tz is up-to-date.

long millis = 1464645007120L;
Instant instant = Instant.ofEpochMilli ( millis );  // UTC, always.

ZoneId zoneId = ZoneId.of ( "America/Santiago" );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant , zoneId );

Dump to console.

System.out.println ( "millis: " + millis + "  | instant: " + instant + " | zoneId: " + zoneId + " | zdt: " + zdt );

millis: 1464645007120 | instant: 2016-05-30T21:50:07.120Z | zoneId: America/Santiago | zdt: 2016-05-30T18:50:07.120-03:00[America/Santiago]

You can see the offset in the last part is -03:00. So it seems to me that the current version of Java released mid-April is unfortunately not up-to-date with the Chile changes announced in March.

From my readings it seems that today’s (2016-05-30) correct offset for America/Santiago should be -04:00 as seen here on time.is/Santiago.

Usually I would suggest using the Java tzupdater tool. But the current download is labeled with the word 2015 so I presume it is not up-to-date either.

I suggest filing a bug report at the Java team at Oracle.

Workaround

It may be a while before you can obtain a corrected version of the tz database (though perhaps you could hack your own update -- I do not know). Until then, as a workaround, perhaps you could specify your own offset in Java code using the ZoneOffset class, a subclass of ZoneId that represents only the offset-from-UTC without any rules for adjusting for anomalies such as DST. I’ve not thought through the implications; be wary.

ZoneOffset zoneOffset = ZoneOffset.of( -4 ); // Negative four hours for '-04:00'.