Reputation: 8286
I am having trouble with converting old dates from java.time.LocalDateTime to java.util.Date
I tried a lot of variation and it still has the same shifted dates. I would assume that it is some weird calendar performed but it is failing.
Date to parse 1800-01-01 00:00:00
I used a very simple convert function.
Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
TimeZone | a.Converted via SimpleDateFormat | b.Converted via DateFormatter to LocalDateTime to java.util.Date |
---|---|---|
Asia/Tokyo | 1800-01-01 00:00:00 JST | 1799-12-31 23:41:01 JST |
Europe/Brussels | 1800-01-01 00:00:00 CET | 1800-01-01 00:04:30 CET |
Australia/Sydney | 1800-01-01 00:00:00 AEST | 1799-12-31 23:55:08 AEST |
UTC | 1800-01-01 00:00:00 UTC | 1800-01-01 00:00:00 UTC |
a. Convert String to java.util.Date via SimpleDateFormat
b. Convert String to java.time.LocalDatetime via DateFormatter, then convert it to java.util.Date
Now I see it only works for the UTC timezone, I cannot just change the software timezone as it will mess with the others. Anyone know any other way to convert a java.time.LocalDateTime to java.util.Date for an old day?
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
===== following is added after sweeper's answer to illustrate it is not for all ====
The curious thing is it only happens to really old dates, it does not happen to 1900-01-01 00:00:00 but have not check yet at which point the trouble started. I was thinking that maybe because of and adjustment / change at some point in 18XX year.
System.out.println(ZoneId.of("Asia/Tokyo").getRules().getOffset(LocalDateTime.parse("1800-01-01T00:00:00")));
SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
System.out.println(TimeZone.getTimeZone("Asia/Tokyo").getOffset(format1.parse("1800-01-01T00:00:00").getTime()));
System.out.println(ZoneId.of("Asia/Tokyo").getRules().getOffset(LocalDateTime.parse("1900-01-01T00:00:00")));
SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
System.out.println(TimeZone.getTimeZone("Asia/Tokyo").getOffset(format2.parse("1900-01-01T00:00:00").getTime()));
Results to
+09:18:59
32400000
+09:00
32400000
Upvotes: 5
Views: 1049
Reputation: 274650
Similar to this question, the old API disagrees with ZoneId.systemDefault()
about what offsets those locations should be at on the date 1800-01-01.
You can see this in action:
System.out.println(ZoneId.of("Asia/Tokyo").getRules().getOffset(LocalDateTime.parse("1800-01-01T00:00:00")));
var format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
System.out.println(TimeZone.getTimeZone("Asia/Tokyo").getOffset(format.parse("1800-01-01T00:00:00").getTime()));
Output:
+09:18:59
32400000
As said in the linked post, +09:18:59 is the Local Mean Time in Japan, and 32400000ms is exactly 9 hours. This is because the old APIs don't support Local Mean Time. Note that Japan standardised their timezones in 1888, which explains why the outputs from the two APIs are consistent for 1900-01-01.
So ZoneId
thinks the offset of Asia/Tokyo is 18 minutes and 59 seconds more than what TimeZone
(which is used by SimpleSateFormat
and Date.toString
and so on) thinks it is.
This is exactly why there is a "shift" in the output. ldt.atZone(...)
generates
1800-01-01T00:00:00+09:18:59
and toInstant
turns this into an instant.
Assuming you are using Date.toString
or some other old API that uses TimeZone
to generate the string, the old API would think this instant is at +09:00:00 instead! What is the above date at +09:00:00? Well, just subtract the 18 minutes and 59 seconds (just like how you would subtract 9 hours to convert a UTC+9 date to UTC+0)!
And that's how you get:
1799-12-31 23:41:01
As for solutions, there really is nothing wrong with the "shifted" Date
that you got. If you just convert it back to a ZonedDateTime
and format it with DateTimeFormatter
for display, everything should work as normal. In fact, if you can, consider not converting to Date
at all, and use Instant
s instead.
If you cannot do that, I would suggest that you should not mix the two APIs.
Upvotes: 6