I have noticed strange behavior of date and time in java. I have the following code:
public class TestDateTime {
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Helsinki"));
Calendar calendar = GregorianCalendar.getInstance();
assert(calendar.getTimeZone().equals(TimeZone.getDefault()));
//Set 1899-12-30T23:00:00
calendar.set(1899,11,30,23,0,0);
calendar.set(Calendar.MILLISECOND,0);
long timeInMillis = calendar.getTimeInMillis();
java.util.Date calendarDateTime = new java.util.Date(timeInMillis);
LocalDateTime localDateTime = LocalDateTime.ofInstant(ofEpochMilli(timeInMillis), ZoneId.systemDefault());
System.out.println("Time in millis: " + timeInMillis);
System.out.println("Date: " + calendarDateTime.toString());
System.out.println("Local DateTime: " + localDateTime.toString());
}
}
The output is:
Time in millis: -2209086000000
Date: Sat Dec 30 23:00:00 EET 1899
Local DateTime: 1899-12-30T22:39:49
timeInMillis must contain the number of milliseconds passed from 1970-01-01T00:00:00Z
.
The instance of Date class stores number of milliseconds passed from 1970-01-01T00:00:00Z
.
Date.toString()
method returns local date and time for the default timezone.
So the Date.toString()
and LocalDateTime.toString()
must return the same date and time, but we see the difference (more than 20 minutes).
Is this a bug of java, or I use date and time incorrectly in Java?
This is a weirdness caused by Finland time change, see Clock Changes in Helsinki, Finland (Helsingfors) in 1921:
May 1, 1921 - Time Zone Change (HMT → EET)
When local standard time was about to reach Sunday, May 1, 1921, 12:00:00 midnight clocks were turned forward 0:20:11 hours to Sunday, May 1, 1921, 12:20:11 am local standard time instead
Those 20 minutes 11 seconds seem to be what you're observing.
As Jim Garrison said in his answer, LocalDateTime
is correctly handling that, while Calendar
is not.
In reality, it seems that the old TimeZone
is getting the offset wrong, while the new ZoneId
is getting it right, as can be seen in the following test code:
public static void main(String[] args) {
compare(1800, 1, 1, 0, 0, 0);
compare(1899,12,31, 23,59,59);
compare(1900, 1, 1, 0, 0, 0);
compare(1900,12,30, 23, 0, 0);
compare(1921, 4,30, 0, 0, 0);
compare(1921, 5, 1, 0, 0, 0);
compare(1921, 5, 2, 0, 0, 0);
}
private static void compare(int year, int month, int day, int hour, int minute, int second) {
Calendar calendar = new GregorianCalendar();
calendar.clear();
calendar.setTimeZone(TimeZone.getTimeZone("Europe/Helsinki"));
calendar.set(year, month-1, day, hour, minute, second);
Date date = calendar.getTime();
ZonedDateTime zdt = ZonedDateTime.of(year, month, day, hour, minute, second, 0, ZoneId.of("Europe/Helsinki"));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z XXX");
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Helsinki"));
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss z XXX");
System.out.printf("%04d-%02d-%02d %02d:%02d:%02d %s = %d %s = %d %d%n",
year, month, day, hour, minute, second,
sdf.format(date), date.getTime(),
dtf.format(zdt), zdt.toInstant().toEpochMilli(),
date.getTime() - zdt.toInstant().toEpochMilli());
}
Output
1800-01-01 00:00:00 1800-01-01 00:00:00 EET +02:00 = -5364669600000 1800-01-01 00:00:00 EET +01:39 = -5364668389000 -1211000
1899-12-31 23:59:59 1899-12-31 23:59:59 EET +02:00 = -2208996001000 1899-12-31 23:59:59 EET +01:39 = -2208994790000 -1211000
1900-01-01 00:00:00 1900-01-01 00:00:00 EET +02:00 = -2208996000000 1900-01-01 00:00:00 EET +01:39 = -2208994789000 -1211000
1900-12-30 23:00:00 1900-12-30 23:00:00 EET +01:39 = -2177548789000 1900-12-30 23:00:00 EET +01:39 = -2177548789000 0
1921-04-30 00:00:00 1921-04-30 00:00:00 EET +01:39 = -1536025189000 1921-04-30 00:00:00 EET +01:39 = -1536025189000 0
1921-05-01 00:00:00 1921-05-01 00:20:11 EET +02:00 = -1535938789000 1921-05-01 00:20:11 EET +02:00 = -1535938789000 0
1921-05-02 00:00:00 1921-05-02 00:00:00 EET +02:00 = -1535853600000 1921-05-02 00:00:00 EET +02:00 = -1535853600000 0
LocalDateTime
is CORRECT. According to the TZ database, the GMT offset at that date was 1:39:49
:
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Europe/Helsinki 1:39:49 - LMT 1878 May 31
1:39:49 - HMT 1921 May # Helsinki Mean Time
2:00 Finland EE%sT 1983
2:00 EU EE%sT
Historical timezones are incredibly complex, and prior to standardization offsets were inherited from settings based on things like mean solar noon. When going back that far just about any offset is possible, and the IANA TZ database is the master reference for historical data.
From what I can see in the database, the weird offset did not get standardized to 2:00:00
until 1921 when HMT was replaced with EE(S)T.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With