簡體   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