Java TimeUnit.MILLISECONDS.toDays() gives wrong result - java

I'm trying to calculate the difference between two days in amount of days. For some reason comparing 01-03-2013 and 01-04-2013 gives the result 30, as does comparing 01-03-2013 and 31-03-2013
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2013, Calendar.MARCH, 1);
Date start = cal.getTime();
cal.set(2013, Calendar.APRIL, 1);
Date end = cal.getTime();
long days = TimeUnit.MILLISECONDS.toDays(end.getTime() - start.getTime());
System.out.println("!!! Amount of days : " + String.valueOf(days));
>> 30
cal.set(2013, Calendar.MARCH, 1);
start = cal.getTime();
cal.set(2013, Calendar.MARCH, 31);
end = cal.getTime();
days = TimeUnit.MILLISECONDS.toDays(end.getTime() - start.getTime());
System.out.println("!!! Amount of days : " + String.valueOf(days));
>> 30
Why is this?

You will get those results if daylight savings started in your time zone on 31 March.
Between 1 March and 1 April, you have 30 24-hour days and one 23-hour day, because of the start of daylight savings. If you divide the total number of milliseconds by 24 x 60 x 60 x 1000, then you will get 30 plus 23/24. This gets rounded down to 30.

Time Zone
The correct answer by David Wallace explains that Daylight Saving Time or other anomalies affects the results of your code. Relying on default time zones (or outright ignoring time zones) will get you into this kind of trouble.
Make Span Inclusive-Exclusive
Also, the proper way to define a span of time is to make the beginning inclusive and the ending exclusive. So if you want the month of March, you need to go from first moment of first day to first moment of first day after March (April 1).
For lengthy discussion of this idea, see my other answers such as this one and this one.
Here's a diagram of mine lifted from other answers:
Joda-Time
The java.util.Date/Calendar classes bundled with Java are notoriously troublesome. Avoid them. Use either Joda-Time, or in Java 8, the new java.time.* package (inspired by Joda-Time).
The Joda-Time 2.3 library provides classes dedicated to spans of time: Period, Duration, and Interval. That library also has some handy static utility methods, such as Days.daysBetween.
Joda-Time's DateTime objects do know their own time zone, unlike java.util.Date/Calendar which seem to have a time zone but do not.
// Specify a timezone rather than rely on default.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime marchFirst = new DateTime( 2013, DateTimeConstants.MARCH, 1, 0, 0, 0, timeZone );
DateTime aprilFirst = new DateTime( 2013, DateTimeConstants.APRIL, 1, 0, 0, 0, timeZone );
int days = Days.daysBetween( marchFirst, aprilFirst).getDays();
Dump to console…
System.out.println( "marchFirst: " + marchFirst );
System.out.println( "aprilFirst: " + aprilFirst ); // Note the change in time zone offset in the output.
System.out.println( "days: " + days );
When run, notice:
The correct answer: 31
The difference in time zone offset because of Daylight Saving Time in France.
marchFirst: 2013-03-01T00:00:00.000+01:00
aprilFirst: 2013-04-01T00:00:00.000+02:00
days: 31

When I run this version of your code here in United States west coast time zone:
java.util.Calendar cal = java.util.Calendar.getInstance();
cal.clear();
cal.set( 2013, java.util.Calendar.MARCH, 1 );
java.util.Date start = cal.getTime();
cal.set( 2013, java.util.Calendar.APRIL, 1 );
java.util.Date end = cal.getTime();
long days = java.util.concurrent.TimeUnit.MILLISECONDS.toDays( end.getTime() - start.getTime() );
System.out.println( "!!! Amount of days : " + String.valueOf( days ) );
cal.set( 2013, java.util.Calendar.MARCH, 1 );
start = cal.getTime();
cal.set( 2013, java.util.Calendar.MARCH, 31 );
end = cal.getTime();
days = java.util.concurrent.TimeUnit.MILLISECONDS.toDays( end.getTime() - start.getTime() );
System.out.println( "!!! Amount of days : " + String.valueOf( days ) );
I get:
!!! Amount of days : 30
!!! Amount of days : 29
For explanation, see comment by David Wallace on this answer.
Daylight Saving Time (United States) 2013 began at 2:00 AM on Sunday, March 10.

I executed same code in my system it is giving me output as :
!!! Amount of days : 31
please check your code again.

Related

Why does the difference between 30 March and 1 March 2020 erroneously give 28 days instead of 29?

TimeUnit.DAYS.convert(
Math.abs(
new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").parse("30-03-2020 00:00:00").getTime() -
new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").parse("1-03-2020 00:00:00").getTime()
),
TimeUnit.MILLISECONDS)
The result is 28, while it should be 29.
Could the time zone/location be the problem?
The problem is that because of Daylight Saving Time shift (on Sunday, March 8, 2020), there are 28 days and 23 hours between those dates. TimeUnit.DAYS.convert(...) truncates the result to 28 days.
To see the problem (I'm in US Eastern time zone):
SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
long diff = fmt.parse("30-03-2020 00:00:00").getTime() -
fmt.parse("1-03-2020 00:00:00").getTime();
System.out.println(diff);
System.out.println("Days: " + TimeUnit.DAYS.convert(Math.abs(diff), TimeUnit.MILLISECONDS));
System.out.println("Hours: " + TimeUnit.HOURS.convert(Math.abs(diff), TimeUnit.MILLISECONDS));
System.out.println("Days: " + TimeUnit.HOURS.convert(Math.abs(diff), TimeUnit.MILLISECONDS) / 24.0);
Output
2502000000
Days: 28
Hours: 695
Days: 28.958333333333332
To fix, use a time zone that doesn't have DST, e.g. UTC:
SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
long diff = fmt.parse("30-03-2020 00:00:00").getTime() -
fmt.parse("1-03-2020 00:00:00").getTime();
Output
2505600000
Days: 29
Hours: 696
Days: 29.0
The cause of this problem is already mentioned in Andreas's answer.
The question is what exactly you want to count. The fact that you state that the actual difference should be 29 instead of 28, and ask whether "location/zone time could be a problem", reveals what you actually want to count. Apparently, you want to get rid of any timezone difference.
I assume you only want to calculate the days, without time and timezone.
Java 8
Below, in the example of how the number of days between could be calculated correctly, I'm using a class that represents exactly that – a date without time and timezone – LocalDate.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d-MM-yyyy HH:mm:ss");
LocalDate start = LocalDate.parse("1-03-2020 00:00:00", formatter);
LocalDate end = LocalDate.parse("30-03-2020 00:00:00", formatter);
long daysBetween = ChronoUnit.DAYS.between(start, end);
Note that ChronoUnit, DateTimeFormatter and LocalDate require at least Java 8, which is not available to you, according to the java-7 tag. However, it perhaps is to future readers.
As mentioned by Ole V.V., there's also the ThreeTen Backport, which backports Java 8 Date and Time API functionality to Java 6 and 7.

How to calculate next week?

I want to precisely calculate the time one week from a given date, but the output I get back is one hour early.
code:
long DURATION = 7 * 24 * 60 * 60 * 1000;
System.out.println(" now: " + new Date(System.currentTimeMillis()));
System.out.println("next week: " + new Date(System.currentTimeMillis() + DURATION));
output:
now: Wed Sep 16 09:52:36 IRDT 2015
next week: Wed Sep 23 08:52:36 IRST 2015
How can I calculate this correctly?
Never, ever rely on millisecond arithmetic, there are too many rules and gotchas to make it of any worth (even over a small span of time), instead use a dedicated library, like Java 8's Time API, JodaTime or even Calendar
Java 8
LocalDateTime now = LocalDateTime.now();
LocalDateTime then = now.plusDays(7);
System.out.println(now);
System.out.println(then);
Which outputs
2015-09-16T15:34:14.771
2015-09-23T15:34:14.771
JodaTime
LocalDateTime now = LocalDateTime.now();
LocalDateTime then = now.plusDays(7);
System.out.println(now);
System.out.println(then);
Which outputs
2015-09-16T15:35:19.954
2015-09-23T15:35:19.954
Calendar
When you can't use Java 8 or JodaTime
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
cal.add(Calendar.DATE, 7);
Date then = cal.getTime();
System.out.println(now);
System.out.println(then);
Which outputs
Wed Sep 16 15:36:39 EST 2015
Wed Sep 23 15:36:39 EST 2015
nb: The "problem" you seem to be having, isn't a problem at all, but simply the fact that over the period, your time zone seems to have entered/exited day light savings, so Date is displaying the time, with it's correct offset
Try this
Calendar cal = Calendar.getInstance();
System.out.println(cal.getTime());
cal.add(Calendar.DAY_OF_MONTH, 7);
System.out.println(cal.getTime());
The difference is because of the different timezone. IRDT is +0430 and IRST is +0330
To overcome this issue you can use the JodaTime.
LocalDateTime now = LocalDateTime.now();
LocalDateTime nextweek = now.plusDays(7);
System.out.println(now);
System.out.println(nextweek);
As other said. It would be better to use Calendar or JodaTime library. But the question is why you were not getting the desired result. It was because currentTimeMillis() calculates time between "computer time" and coordinated universal time (UTC). Now consider following case.
long DURATION = 7 * 24 * 60 * 60 * 1000;
Date now = new Date();
Date nextWeek = new Date(now.getTime() + DURATION);
System.out.println(" now: " + now);
System.out.println("next week: " + nextWeek);
here Date.getTime() calculate time from 00:00:00 GMT every time and then when converted to string will give time for your local time zone.
Edit :
I was wrong. The reason is as simon said.
The actual "why" is that IRDT (Iran Daylight Time) ends on September
22nd. That's why the first date (September 16th) in the OP's post is
displayed as IRDT and the second date (September 23rd) is displayed as
IRST. Because IRST (Iran Standard Time) is one hour earlier than IRDT
the time displayed is 08:52:36 instead of 09:52:36.

Getting the day of the week from user input

Date date= (new GregorianCalendar(year, month, day)).getTime();
SimpleDateFormat f = new SimpleDateFormat("EEEE");
String dow=f.format(date);
System.out.print("This date is a "+dow);
I have the user input a month(1-12) a day(1-31) and a year(1600-2400)
It works fine only it displays the wrong day. For example it says that Jan 1st 2014 is a Saturday but it was a Wednesday.
It is probably because I didn't factor in leap years but I don't have a clue to go about doing that. Neither do I know how to tell it how many days in each month. An array?
Hopefully minimal lines as well.
Thanks so much!!! This has been bugging me for an hour +. And something so simple, I should have figured. I must be tired.
Thanks!!!!!!!
Month is Zero based. Try,
Date date= (new GregorianCalendar(year, month-1, day)).getTime();
SimpleDateFormat f = new SimpleDateFormat("EEEE");
String dow=f.format(date);
The answer by Shashank Kadne is correct.
Joda-Time
FYI, this work is simpler and cleaner using the Joda-Time 2.3 library.
Joda-Time uses sensible one-based counting for things such as:
Month-of-YearJanuary = 1, February = 2, and so on.
Day-of-WeekMonday = 1, Sunday = 7. (Standard ISO 8601 week)
Joda-Time DateTime objects know their own time zone, unlike java.util.Date objects.
Joda-Time leverages a specified Locale object to render localized strings.
Example Code
// Specify a time zone rather than rely on default.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
int year = 2014;
int month = 1; // Sensible one-based counting. January = 1, February = 2, …
int dayOfMonth = 2;
DateTime dateTime = new DateTime( year, month, dayOfMonth, 0, 0, 0, timeZone );
// Day-of-week info.
int dayOfWeekNumber = dateTime.getDayOfWeek(); // Standard week (ISO 8601). Monday = 1, Sunday = 7.
DateTime.Property dayOfWeekProperty = dateTime.dayOfWeek();
String dayOfWeekName_Short = dayOfWeekProperty.getAsShortText( Locale.FRANCE );
String dayOfWeekName_Long = dayOfWeekProperty.getAsText( Locale.FRANCE );
Dump to console…
System.out.println( "dateTime: " + dateTime );
System.out.println( "dayOfWeekNumber: " + dayOfWeekNumber );
System.out.println( "dayOfWeekName_Short: " + dayOfWeekName_Short );
System.out.println( "dayOfWeekName_Long: " + dayOfWeekName_Long );
When run…
dateTime: 2014-01-02T00:00:00.000+01:00
dayOfWeekNumber: 4
dayOfWeekName_Short: jeu.
dayOfWeekName_Long: jeudi
Without Time & Time Zone
If you truly want only date without any time or time zone, then write similar code but with the LocalDate class.

How to convert from java.util.date to JodaTime and get same date

I follow this question: Convert from java.util.date to JodaTime
I have date: Sun Jan 01 00:00:00 CET 1854
now I want to convert it to joda datetime:
DateTime dateTime = new DateTime(date);
and now when I print this date I got:
1853-12-31T23:57:44.000+00:57:44
what is wrong and why my date changed ? How I can get the same date ?
UPDATE:
I get date using calendar:
Calendar cal1 = Calendar.getInstance();
cal1.set(1854, 0, 1, 0, 0, 0);
cal1.getTime()
UPDATE2:
propably there is problem with milseconds:
Calendar cal1 = Calendar.getInstance();
cal1.set(1854, 0, 1, 0, 0, 0);
DateTime start = new DateTime(1854, 1, 1, 0, 0, 0);
System.out.println(start.getMillis());
System.out.println(cal1.getTime().getTime());
because this code return:
-3660598664000
-3660598799438
but I dont know why
UPDATE3:
Joda-Time uses the accurate time-zone database, which has Local Mean Time (LMT) for years before time-zones started. To quote Wikipedia:
Local mean time is a form of solar time that corrects the variations of local apparent time, forming a uniform time scale at a specific longitude.
The JDK doesn't use LMT, thus the times differ.
ok I solve it. Is isnt nice but it works what is important
Calendar calendar = Calendar.getInstance();
calendar.setTime(datum);
DateTime current = new DateTime(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1,
calendar.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
IF you have the date as type of java.util.date you can use
java.util.Date date = ....
DateTime dateTime = new DateTime(date.getTime());
this code
Calendar cal1 = Calendar.getInstance();
cal1.set(1854, 0, 1, 0, 0, 0);
DateTime start = new DateTime(cal1.getTime());
System.out.println(start);
System.out.println(cal1.getTime());
outputs :
1854-01-01T00:00:00.941Z
Sun Jan 01 00:00:00 GMT 1854
I imagine the millisecond discrepancy is calendar choosign the saem time of day as now, to start the milliseconds count on. Whereas joda-time chooses midnight. Or something similarly obtuse. I try and stay away from java's built in Calendar and Date, they are an abomination.
The Answer by JodaStephen is correct and should be accepted.
The Joda-Time team have instructed us to migrate to the java.time framework built into Java 8 and later. So I was curious to try this problem in java.time and compare results.
The Question says the input data is for CET offset-from-UTC, but the code in the Question ignores that fact. My code below uses an offset of one hour ahead of UTC to account for CET.
java.time
The CET means one hour ahead of UTC. Since we have only an offset-from-UTC and not a full time zone, I use the OffsetDateTime class, for +01:00.
LocalDateTime localDateTime = LocalDateTime.of ( 1854 , 1 , 1 , 0 , 0 , 0 , 0 ); // The nineteenth century.
ZoneOffset offset = ZoneOffset.of ( "+01:00" ); // “CET” means one hour ahead of UTC.
OffsetDateTime odt = OffsetDateTime.of ( localDateTime , offset );
Instant instant = odt.toInstant (); // A moment on the timeline in UTC, with resolution in nanoseconds.
long m = instant.toEpochMilli ();
System.out.println ( "odt: " + odt + " | millis: " + m );
odt: 1854-01-01T00:00+01:00 | millis: -3660598800000
Joda-Time
Same code, but using Joda-Time 2.9.3.
DateTimeZone zone = DateTimeZone.forOffsetHoursMinutes ( 1 , 0 );
DateTime dateTime = new DateTime ( 1854 , 1 , 1 , 0 , 0 , zone );
long millis = dateTime.getMillis ();
System.out.println ( "dateTime: " + dateTime + " | millis: " + millis );
dateTime: 1854-01-01T00:00:00.000+01:00 | millis: -3660598800000
Result is same as java.time.
java.util.Calendar
For comparison only. Normally you should avoid the old java.util.Date/.Calendar classes as they have proven to be poorly designed, confusing, and troublesome.
Calendar calendar = Calendar.getInstance ();
calendar.set ( 1854 , 0 , 1 , 0 , 0 , 0 );
TimeZone zone = TimeZone.getTimeZone ( "GMT+01:00" );
calendar.setTimeZone ( zone );
long millis = calendar.getTimeInMillis ();
System.out.println ( "calendar: " + calendar + " | millis: " + millis );
calendar: java.util.GregorianCalendar[time=-3660598799715,areFieldsSet=true,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="GMT+01:00",offset=3600000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=1854,MONTH=0,WEEK_OF_YEAR=1,WEEK_OF_MONTH=1,DAY_OF_MONTH=1,DAY_OF_YEAR=1,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=285,ZONE_OFFSET=3600000,DST_OFFSET=0] | millis: -3660598799715
Different results. Here we have -3660598799715 versus -3660598800000 in java.time & Joda-Time, a difference of 285.
Europe/Brussels
I also tried all three with a time zone of Europe/Brussels rather than an offset-from-UTC.
In java.time. Using ZonedDateTime class rather than OffsetDateTime.
LocalDateTime localDateTime = LocalDateTime.of ( 1854 , 1 , 1 , 0 , 0 , 0 , 0 ); // The nineteenth century.
ZoneId zoneId = ZoneId.of ( "Europe/Brussels" );
ZonedDateTime zdt = ZonedDateTime.of ( localDateTime , zoneId );
Instant instant = zdt.toInstant (); // A moment on the timeline in UTC, with resolution in nanoseconds.
long m = instant.toEpochMilli ();
System.out.println ( "zdt: " + zdt + " | millis: " + m );
zdt: 1854-01-01T00:00+00:17:30[Europe/Brussels] | millis: -3660596250000
In Joda-Time. Only first line is different.
DateTimeZone zone = DateTimeZone.forID ( "Europe/Brussels" );
dateTime: 1854-01-01T00:00:00.000+00:17:30 | millis: -3660596250000
In java.util.Calendar. Some code except for the TimeZone line:
TimeZone zone = TimeZone.getTimeZone ( "Europe/Brussels" );
calendar: java.util.GregorianCalendar[time=-3660598799151,areFieldsSet=true,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Brussels",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=184,lastRule=java.util.SimpleTimeZone[id=Europe/Brussels,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=1854,MONTH=0,WEEK_OF_YEAR=1,WEEK_OF_MONTH=1,DAY_OF_MONTH=1,DAY_OF_YEAR=1,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=849,ZONE_OFFSET=3600000,DST_OFFSET=0] | millis: -3660598799151
All three using Europe/Brussels differ from their version with offset of +01:00.
And again java.time & Joda-Time agree with each other (-3660596250000), while differing from Calendar (-3660598799151), a difference of 2,549,151 (about 42 and a half minutes).

Unexpected conversion from java.sql.Timestamp to joda's LocalDate

i'm having a problem while converting Timestamp objects to joda's LocalTime.
See example below:
public static void main(String[] args) {
Timestamp t = Timestamp.valueOf("1111-11-11 00:00:00");
System.out.println(t); //-- prints '1111-11-11 00:00:00.0'
System.out.println(new LocalDate(t)); //-- prints '1111-11-17'
Calendar calendar = Calendar.getInstance();
calendar.setTime(t);
System.out.println(LocalDate.fromCalendarFields(calendar)); //-- prints '1111-11-11'
}
I could not determine why 'new LocalDate(t)' results in '1111-11-17'. Can anyone help me on that?
I notice this "problem" while using joda-time-hibernate to populate my bean's property of type LocalDate.
This indeed has to do with the different types of calendars. java.sql.Timestamp, like java.util.Date, don't have any calendar information, as they are stored solely as a number, the number of milliseconds since 1970, with the implied assumption that there was a jump between October 4 and October 15 1582, when the Gregorian calendar was officially adopted, replacing the old Julian calendar. So, you can't possibly have a Date (or Timestamp) object representing October 7, 1582. If you try to create such a date, you'll automatically end up 10 days later. For example:
Calendar c = Calendar.getInstance();
c.setTimeZone(TimeZone.getTimeZone("UTC"));
c.set(1582, Calendar.OCTOBER, 7, 0, 0, 0);
c.set(Calendar.MILLISECOND, 0);
d = c.getTime();
System.out.println("Date: " + d);
// Prints:
// Date: Sun Oct 17 00:00:00 GMT 1582
In other words, Date objects have an implied Julian+Gregorian chronology, automatically switching between those two.
JodaTime is a bit smarter, it supports several Chronologies, including a continuing Julian, a proleptic Gregorian, a mixed Julian+Gregorian, and a standard ISO Chronology which is almost identical to the Gregorian one. If you read the JavaDoc of the LocalDate.fromCalendarFields method, you'll see that it mentions that:
This factory method ignores the type of the calendar and always creates a LocalDate with ISO chronology.
The mixed Julian+Gregorian chronology behaves like the implicit Java dates, with an automatic switch between the two different calendars. The pure chronologies assume that their calendar system is forever in use, so for example it assumes that the Gregorian calendar has been used since the start of time.
Let's see how each Chronology treats the 1111-11-11 date:
Calendar c = Calendar.getInstance();
c.setTimeZone(TimeZone.getTimeZone("UTC"));
c.set(1111, Calendar.NOVEMBER, 11, 0, 0, 0);
c.set(Calendar.MILLISECOND, 0);
Date d = c.getTime();
System.out.println("Date: " + d + " (" + d.getTime() + " milliseconds)");
System.out.println("ISO: " + new DateTime(d, ISOChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Julian+Gregorian: " + new DateTime(d, GJChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Julian: " + new DateTime(d, JulianChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Gregorian: " + new DateTime(d, GregorianChronology.getInstance(DateTimeZone.forID("UTC"))));
Ends up as:
Date: Sat Nov 11 00:00:00 GMT 1111 (-27079747200000 milliseconds)
ISO: 1111-11-18T00:00:00.000Z
Julian+Gregorian: 1111-11-11T00:00:00.000Z
Julian: 1111-11-11T00:00:00.000Z
Gregorian: 1111-11-18T00:00:00.000Z
As you can see, the two modern chronologies (ISO and Gregorian) report the correct date if they would have been in use since from the start, while the two that use the Julian calendar report the date as it was known back then, although in hindsight we know it to be off by 7 days compared to the true equinox date.
Let's see what happened around the switch:
c.set(1582, Calendar.OCTOBER, 15, 0, 0, 0);
d = c.getTime();
System.out.println("Date: " + d + " (" + d.getTime() + " milliseconds)");
System.out.println("ISO: " + new DateTime(d, ISOChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Julian+Gregorian: " + new DateTime(d, GJChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Julian: " + new DateTime(d, JulianChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Gregorian: " + new DateTime(d, GregorianChronology.getInstance(DateTimeZone.forID("UTC"))));
ends up as:
Date: Fri Oct 15 00:00:00 GMT 1582 (-12219292800000 milliseconds)
ISO: 1582-10-15T00:00:00.000Z
Julian+Gregorian: 1582-10-15T00:00:00.000Z
Julian: 1582-10-05T00:00:00.000Z
Gregorian: 1582-10-15T00:00:00.000Z
So the only one that's left behind is the Julian calendar. That was a valid date in all the countries that didn't accept the Gregorian calendar yet, which back then was a lot of countries. Greece made the switch in 1923...
One millisecond before that, the date was:
c.add(Calendar.MILLISECOND, -1);
d = c.getTime();
System.out.println("Date: " + d + " (" + d.getTime() + " milliseconds)");
System.out.println("ISO: " + new DateTime(d, ISOChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Julian+Gregorian: " + new DateTime(d, GJChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Julian: " + new DateTime(d, JulianChronology.getInstance(DateTimeZone.forID("UTC"))));
System.out.println("Gregorian: " + new DateTime(d, GregorianChronology.getInstance(DateTimeZone.forID("UTC"))));
Meaning:
Date: Thu Oct 04 23:59:59 GMT 1582 (-12219292800001 milliseconds)
ISO: 1582-10-14T23:59:59.999Z
Julian+Gregorian: 1582-10-04T23:59:59.999Z
Julian: 1582-10-04T23:59:59.999Z
Gregorian: 1582-10-14T23:59:59.999Z
The ISO and Gregorian chronologies report a date that didn't actually exist in the Gregorian calendar, since there was no Gregorian calendar before October 15, yet this date is valid in an extended, proleptic Gregorian calendar. It's like finding a BCE date inscribed on a BCE monument... Nobody knew that they were before Christ before Christ was even born.
So, the root of the problem is that a date string is ambiguous, since you don't know in which calendar you're measuring. Is the year 5772 a year in the future, or is it the current Hebrew year? Java assumes a mixed Julian+Gregorian calendar. JodaTime provides extensive support for different calendars, and by default it assumes the ISO8601 chronology. Your date is automatically converted from the Julian calendar in use in 1111 to the ISO chronology that we currently use. If you want your JodaTime-enhanced timestamps to use the same chronology as the java.sql.Timestamp class, then explicitly select the GJChronology when constructing JodaTime objects.
The reason this is happening for dates several centuries in the past has to do with the (worldwide) switch to the Gregorian Calendar from the Julian Calendar. Basically, as countries implemented the new calendar system in a piecemeal fashion, they would lose days or weeks (and in some cases, months) in the process. In Spain, for example, 4 October 1582 in the Julian calendar was followed immediately by 15 October 1582 in the Gregorian calendar. Wikipedia has a decent discussion of the topic.
As long as your application does not deal with dates too far in the past, you won't have to deal with discrepancies related to this. As you mentioned in your comment, System.out.println(new LocalDate(Timestamp.valueOf("1999-11-11 00:00:00"))) correctly outputted 1999-11-11. If you need to work with these older dates, I'd suggest looking into an alternative date for the Julian calendar.
(As an aside, I'd be curious to see what would happen if you asked for the local date, in Spain, of 5 October 1582 ... a day which never happened.)

Categories