繁体   English   中英

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

[英]Cannot Convert Date '0001-01-01' from Java to C# correctly

我尝试提供一个工具将 datetime 从 Java 转换为 C#。但是有一个严重的问题。

在 Java 中,我通过 java.sql.Date 从 SQL 服务器数据库中读取“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/do.net/api/system.globalization.juliancalendar?view.net-5.0#remarks中找到了备注

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

实际原因是:

  • C#一直使用公历。
  • Java在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 object 在当时假定为 UTC 偏移 +08:00 的时区中创建,也许只是 Etc/GMT-8 . 根据这个假设,该值在历史上是正确的,因为当时使用的是 Julian 日历,而Date确实使用了它。

我不知道 C# 使用的 .NET 类,但我认为这几天的错误很可能是由于他们使用了 proleptic 公历,也就是说,假装过去一直使用公历,即使它没有在1582之前就存在了。 现代 Java 日期和时间 API 执行此操作,因此为您提供通常相差几天的毫秒值。

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

Output:

-62,135,625,600,000

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

关联

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

您忘记考虑时区偏移量。

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

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

Output

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));

Output

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

这是正确的公元 1 年。

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

对于日期0001-01-01 00:00 UTC ,正确的毫秒值是-62135769600000 C# DateTime class 将拒绝任何小于该值的内容。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM