I need to transform a .NET DateTime
to an equivalent Java Calendar
representation.
The .NET DateTime
uses Ticks
since Jan 1st 0001 (the .NET epoch) as the underlying representation.
The Java GregorianCalendar
uses milliseconds since Jan 1st 1970 (the Java (or Unix) epoch). The value is negative for dates before the Java epoch, as expected.
Here I'm transforming the DateTime
representation in millis since the Java epoch:
var dt = new DateTime(1,2,3); //way, way back.
var javaEpoch = new DateTime(1970, 1, 1);
var javaMillis = (dt - javaEpoch).Ticks / TimeSpan.TicksPerMillisecond;
dt.ToString("MM/dd/yyyy").Dump(); // .Dump() is provided by LinqPad.
javaMillis.Dump(); // Use Console.WriteLine(...)
// for a regular console app.
This outputs:
02/03/0001
-62132745600000
Now copy-paste the milliseconds value in this Java snippet:
java.util.Calendar cal = new java.util.GregorianCalendar();
cal.setTimeInMillis(-62132745600000L);
java.text.SimpleDateFormat df = new java.text.SimpleDateFormat();
df.applyPattern("MM/dd/yyyy");
System.out.println(df.format(cal.getTime()));
This outputs:
02/05/0001
I guess my question is: How am I supposed to get a valid milliseconds value from a DateTime, from which I can correctly construct a Java Calendar?
...with the implied sub-question "what is really going on in here?"
EDIT: I played with DateTimeValues around the missing date range from Julian to Gregorian calendar (Oct 4 1582 is "followed" by Oct 15 1582).
For dates more recent than Oct 15 1582, the conversion seems to work fine.
...But around the missing range, DateTime starts (or rather, doesn't start) to act funny:
var timespan = new DateTime(1582, 10, 15) - new DateTime(1582, 10, 4);
returns a TimeSpan
of 11 days, so the hole is not taken into consideration by the DateTime operators. What gives? I thought the underlying implementation is based on System.Globalization.GregorianCalendar
.
Surprisingly, no one has actually answered the question of why a "hole" exists in the Gregorian Calendar between October 4, 1582 and October 15, 1582. The interesting answer is in the history of the adoption of the Gregorian Calendar as a reform in 1582 to the Julian Calendar. See Wikipedia's article on Gregorian Calendar for details. The last paragraph in the History: Gregorian Reform paragraph states
- Gregory (Pope Gregory XIII) dropped 10 days to bring the calendar back into synchronisation with the seasons. Accordingly, when the new calendar was put in use, the error accumulated in the 13 centuries since the Council of Nicaea was corrected by a deletion of ten days. The Julian calendar day Thursday, 4 October 1582 was followed by the first day of the Gregorian calendar, Friday, 15 October 1582 (the cycle of weekdays was not affected).*
In effect, the dates from October 5, 1582 to October 14, 1582 do not exist in the Gregorian Calendar. I suspect that the .Net Framework uses a generalized formula and does not account for the difference when dealing with dates prior to October 15, 1582, whereas the Java library does account for it.
From the Java documentation:
[...] dates obtained using
GregorianCalendar
are historically accurate only from March 1, 4 AD onward, when modern Julian calendar rules were adopted
Considering your DateTime
subtraction, it's only a Ticks
difference, there is absolutely no notion of a specific calendar here. The implementation is basically return new TimeSpan(x.Ticks - y.Ticks)
.
You might be better at simply outputting then parsing an ISO-8061 date/time such as 0001-02-03T00:00:00Z
, which has no ambiguity, rather than relying on the internal representation.
Answering the 'why':
From the (decompiled - thanks dotPeek!) .NET 4 source code (comments are mine):
public static DateTime operator -(DateTime d, TimeSpan t)
{
//range checks
long internalTicks = d.InternalTicks;
long num = t._ticks;
if (internalTicks < num || internalTicks - 3155378975999999999L > num)
throw new ArgumentOutOfRangeException("t",
Environment.GetResourceString("ArgumentOutOfRange_DateArithmetic"));
else
//plain arithmetic using the Ticks property of the two dates.
return new DateTime((ulong) (internalTicks - num) | d.InternalKind);
}
So yeah, absolutely no special 'gregorian' treatment for DateTime operators.
About the 'how to fix':
I ended up using something along these lines: (pseudo-Java)
Calendar cal = new GregorianCalendar();
cal.set(dt.Year, dt.Month - 1 /*it's 0-based*/, dt.Day, dt.Hour, dt.Minute, dt.Second);
cal.set(Calendar.MILLISECOND, dt.Millisecond);
来源:https://stackoverflow.com/questions/15949938/convert-date-between-java-and-net-2-days-off