Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting Date to LocalDate returning strange results around 200AD

I'm getting inconsistent results when converting Dates to LocalDates, around the year 200. Using the following code to do the conversion:

  private LocalDate toLocalDate(Date localDate)
  {
    return LocalDateTime.ofInstant(localDate.toInstant(), ZoneId.systemDefault()).toLocalDate();
  }

My ZoneId.systemDefault() is Africa/Harare, which matches the CAT used in the test. The test case I run is

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US);
String dateString = "Tue Jan 01 00:00:00 CAT 200";
String dateString2 = "Tue Jan 01 00:00:00 CAT 201";
String dateString3 = "Wed Dec 31 00:00:00 CAT 200";

System.out.println(toLocalDate(simpleDateFormat.parse(dateString)));
System.out.println(toLocalDate(simpleDateFormat.parse(dateString2)));
System.out.println(toLocalDate(simpleDateFormat.parse(dateString3)));

My expected output for this would be

0200-01-01
0201-01-01
0200-12-31

Or, if not that, at least consistently incorrect values. The actual results are

0199-12-31
0201-01-01
0200-12-31

So it seems that the first one is being rolled back slightly, possibly the two hours corresponding to the CAT timezone? But why does this only happen on the one case? Doing the same experiment with the year 2000 does not produce the same error.

like image 437
Evan Knowles Avatar asked Oct 01 '15 07:10

Evan Knowles


Video Answer


1 Answers

Stephen has provided an explanation in the comment. Basically, java.util.Date uses a calendar system which cuts over between the Julian calendar system and the Gregorian calendar system in 1582, skipping 10 days. So dates in 1582 or before will exhibit discrepancies - but the size of the discrepancy will vary over time - by 3 days every 400 years, on average. It so happens that between 200 and 400AD, you don't see this because that corresponds to when the discrepancy is 0.

Here's a short but complete program to demonstrate the problem:

import java.time.*;
import java.util.*;

public class Test {
    public static void main(String[] args) throws Exception {
        // Value obtained with Noda Time: should be 0199-12-31T22:00:00Z.
        long millis = -55855792800000L;
        Instant instant = Instant.ofEpochMilli(millis);
        Date date = new Date(millis);
        System.out.println(instant);
        System.out.println(date);
    }
}

Output on my machine:

0199-12-31T22:00:00Z
Tue Jan 01 22:00:00 GMT 200

This is all complicated by the problems in your initial code of assuming CAT and Africa/Harare are the same (at that point in time, Africa/Harare is regarded as having an offset of +02:10) and the incorrect day names in your strings - but it's the bug in Java which is causing the issue here.

I suggest you perform all your parsing using the java.time.format classes - then I'd hope you won't get this inconsistency.

like image 73
Jon Skeet Avatar answered Sep 21 '22 13:09

Jon Skeet