无法将日期'0001-01-01'从 Java 正确转换为 C#



我试图提供一个将日期时间从Java转换为C#的工具。但这是一个严重的问题。

在Java中,我通过Java.SQL.Date从SQL Server数据库读取"0001-01-01",并获得毫秒-62135798400000

我还考虑了时区偏移。

private static long getMilliSecondWithoutTimeZone(long origin) {
return origin + (ZonedDateTime.now().getOffset().getLong(OFFSET_SECONDS) * 1000);
}

最后一毫秒是-62135769600000

在C#中,我使用这毫秒来创建Datetime

var ticks = new DateTime(1970, 1, 1).Ticks + (-62135769600000 * 10000);
var date = new DateTime(ticks);

当代码运行时,它将抛出异常:

System.ArgumentOutOfRangeException: 'Ticks must be between DateTime.MinValue.Ticks and DateTime.MaxValue.Ticks. (Parameter 'ticks')'

然而,根据我的测试,在"1600-01-01"之后的转换是正确的
在"1600-01-01"之前,总会有几天的错误。

这让我很困惑。


我在https://learn.microsoft.com/en-us/dotnet/api/system.globalization.juliancalendar?view=net-5.0#备注

格里高利历是作为儒略历(由儒略历类表示)的替代品而发展起来的,并于1582年10月15日首次在少数文化中引入。在处理区域性采用公历之前的历史日期时,如果原始日历在.NET Framework中可用,则应使用原始日历。例如,丹麦在1700年2月19日(儒略历)或3月1日(公历)从儒略历改为公历。在这种情况下,对于采用公历之前的日期,应使用儒略历。但是,请注意,没有任何文化提供对JulianCalendar类的内在支持。您必须将JulianCalendar类用作独立日历。有关详细信息,请参阅使用日历。

实际原因是:

  • C#一直使用公历
  • 爪哇在1582年10月15日之后使用格里高利历,之前使用儒略历

解决方案:

import java.sql.Date;
import java.time.chrono.IsoChronology;
import java.time.*;
public class Test {
public static Long getMilliSeconds(Date date) {
if (null == date) {
return null;
}
IsoChronology ISO = IsoChronology.INSTANCE;
LocalDate ld = date.toLocalDate();
return ISO.localDateTime(LocalDateTime.of(ld.getYear(), ld.getMonth(), ld.getDayOfMonth(), 0, 0, 0)).toInstant(ZoneOffset.UTC).toEpochMilli();
}
}

您提到的毫秒值-62_135_798_400_000似乎来自于一个老式的java.sql.Date对象,该对象创建于一个时区,该时区当时被假设为UTC偏移量+08:00,可能只是Etc/GMT-8。有了这个假设,这个值在历史上是正确的,因为当时使用的是儒略历,而Date确实使用了这个。

我不知道C#使用的.NET类,但我认为有几天的错误很可能是由于他们使用了令人怀疑的格里高利日历,也就是说,尽管格里高利历在1582之前并不存在,但假装它在过去一直使用。现代的Java日期和时间API做到了这一点,因此为您提供的毫秒值通常相差几天。

long milliseconds = LocalDate.of(1, 1, 1)
.atStartOfDay(ZoneOffset.ofHours(8))
.toInstant()
.toEpochMilli();
System.out.format(Locale.ENGLISH, "%,d%n", milliseconds);

输出:

-62135625600000

比您提到的时间晚了48小时或2天。看看它是否能解决你的问题。

链接

Oracle教程:日期时间解释如何使用java.Time.

您忘记考虑时区偏移。

如果我们将时区设置为UTC,您将看到:

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
System.out.println(new Date(-62135798400000L));

输出

Fri Dec 31 16:00:00 UTC 1

实际上是公元前1年,而不是公元1年。

时间16:00表示时区偏移为8小时,因此如果我们更改为GMT+8,则会得到:

TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
System.out.println(new Date(-62135798400000L));

输出

Sat Jan 01 00:00:00 GMT+08:00 1

这是正确的公元1年。

这意味着您需要将毫秒值调整8小时,即28800000毫秒。

对于日期0001-01-01 00:00 UTC,正确的毫秒值为-62135769600000。任何低于此值的内容都将被C#DateTime类拒绝。

最新更新