I want to parse java.util.Date
to java.time.LocalDate
I found this code, when I searched for the problem:
Date date = new SimpleDateFormat("dd.MM.yyyy").parse("01.01.0001");
date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
if I have the 01.01.0001
as start date, I got problems:
date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
==> (java.time.LocalDate) 0000-12-29
29.12.0000
and 01.01.0001
are not the same.
LocalDate
with the value 01.01.0001
?Informations to the time-zone:
ZoneId.systemDefault()
==> (java.time.ZoneRegion) Europe/Berlin
date.toInstant()
Date date0001 = new SimpleDateFormat("dd.MM.yyyy").parse("01.01.0001");
System.out.println(date0001.toInstant());
==> 0000-12-29T23:00:00Z
Date date1901 = new SimpleDateFormat("dd.MM.yyyy").parse("01.01.1901");
System.out.println(date1901.toInstant());
==> 1900-12-31T23:00:00Z
Europe/Berlin
corresponds to 23:00 the day before at UTC
(no daylight saving time)Date
, SimpleDateFormat
, Calendar
) are using the Julian Calendar up to October 1582, while the new classes in java.time
are using the Proleptic Gregorian CalendarFrom Wikipedia we can see the difference between both, and the following code confirms that:
TimeZone.setDefault(TimeZone.getTimeZone("UTC")); // one error less
for (var year : List.of(1, 101, 201, 1401, 1501, 1601)) {
var date = new Date(year-1900, 0, 10);
var instant = date.toInstant();
System.out.println(date.getTime() + " = " + date);
System.out.println(instant.toEpochMilli() + " = " + instant);
System.out.println();
}
Output:
-62134992000000 = Mon Jan 10 00:00:00 UTC 1 -62134992000000 = 0001-01-08T00:00:00Z -58979232000000 = Sun Jan 10 00:00:00 UTC 101 -58979232000000 = 0101-01-09T00:00:00Z -55823472000000 = Sat Jan 10 00:00:00 UTC 201 -55823472000000 = 0201-01-10T00:00:00Z -17954352000000 = Mon Jan 10 00:00:00 UTC 1401 -17954352000000 = 1401-01-19T00:00:00Z -14798592000000 = Sun Jan 10 00:00:00 UTC 1501 -14798592000000 = 1501-01-20T00:00:00Z -11643696000000 = Wed Jan 10 00:00:00 UTC 1601 -11643696000000 = 1601-01-10T00:00:00Z
We can find the explanation for the difference in the documentation (emphasis added):
Historically, in those countries which adopted the Gregorian calendar first, October 4, 1582 (Julian) was thus followed by October 15, 1582 (Gregorian). This calendar models this correctly. Before the Gregorian cutover,
GregorianCalendar
implements the Julian calendar. The only difference between the Gregorian and the Julian calendar is the leap-year rule. The Julian calendar specifies leap years every four years, whereas the Gregorian calendar omits century years which are not divisible by 400.
The classes defined here represent the principle date-time concepts, including instants, durations, dates, times, time-zones and periods. They are based on the ISO calendar system, which is the de facto world calendar following the proleptic Gregorian rules.
If dealing with dates after 1582, Date#toInstant
, Instant#atOffset
(Instant#atZone
) and OffsetDateTime#toLocalDate
(ZonedDateTime#toLocalDate
) can be used, taking care to use the correct time zone (offset.) Anyway, as already warned, using these classes is strongly discouraged!
Otherwise, for dates before/at 1582, you will need to first check which calendar was (should be) used to represent these dates.
For a Julian Calendar (before 1583), as I already commented, the following or similar code can be used:
var calendar = Calendar.getInstance();
calendar.setTime(date);
var localDate = LocalDate.of(
calendar.get(Calendar.YEAR),
1 + calendar.get(Calendar.MONTH), // months are 0-based
calendar.get(Calendar.DAY_OF_MONTH));