簡體   English   中英

如何處理夏令時時鍾回滾引起的時間歧義

[英]How To Handle Time Ambiguity Caused by Daylight Saving Time Clock Rollback

我有這個場景,我想看看是否有更好的解決方案:

我從系統 A 收到一個時間戳,格式為“yyyy-MM-dd-HH-mm-ss”,沒有時區,但他們可以將其修復為 UTC。

然后我需要通過 SSH 連接到系統 B 以獲取“上次同步時間”,這可能是過去的時間,並且它不返回任何時區,甚至不返回年份(來自備份產品),這是一個示例:

“同步時間:11 月 3 日星期日 01:13”

系統 B 是一個運行一些專有軟件的備份設備,並且有一個內部調度程序,它每 15 分鍾喚醒一次以檢查是否有任何需要從源同步的數據。 如果前一個同步仍在運行,它將讓當前同步完成而不采取進一步行動。 這個“上次同步時間”將在每次同步后更新,因此在最壞的情況下,它不應落后於當前系統時間超過幾個小時。

我需要比較這兩個時間戳以確保 B 上的“上次同步時間”晚於 A。如果不是,我需要等待並重試查詢,直到 B 晚於 A 才能繼續下一步。

我將 Java 程序放在 Linux 機器上,當請求來自 A 時,它向 B 查詢“上次同步時間”,然后立即向 B 發出“日期”命令以獲取當前系統時間,然后返回格式為“EEE MMM dd kk:mm:ss z yyyy”的當前系統時間,例如,

“2019 年 6 月 14 日星期五 21:07:07 EDT”,

因此程序可以應用該輸出的年份和時區來完成“上次同步時間”的時間戳。 該程序確實處理了“上次同步時間”和“日期”輸出跨越一年邊界的情況。

它工作正常,除非夏季夏令時結束,並且時鍾回滾(凌晨 2 點回到凌晨 1 點),有一個一小時的窗口時間會不明確:

例如,我從系統 A 獲得了一個凌晨 1:20 的時間映射(已經從 UTC 轉換為本地),然后當我從系統 B 獲得一個凌晨 1:30 的時間映射時,我無法判斷 B 是否晚於 A。它可能是 1 :30 在時鍾改變之前。 系統 B 的當前時區並沒有說明“上次同步時間”在哪個時區。

我們要求用戶將所有系統切換到UTC時間,因此我們無需擔心時鍾調整。 但這對環境的影響太大了。

另一種選擇是我專門處理一小時,然后等到“上次同步時間”時間戳超過凌晨 2 點。 為此,我需要在每次進行時間戳比較時檢查代碼是否為特定小時,我認為這不是最佳的。 但我想不出更好的辦法。

任何建議都非常感謝。 如果這是要走的路,是否有一個很好的 Java 庫來確定夏令時切換日期? 太感謝了!

我看不出你能完全確定。 “11 月 3 日”可能是 2002 年、2013 年或 2019 年,甚至更早的歷史。 一種方法是,如果您可以假設上次同步時間不超過您獲得的當前系統 B 系統時間之前的幾年(也不該時間之后),並且如果您可以檢測到則報告錯誤這不是在這幾年內。

夏令時(夏令時)結束的訣竅是如果有任何歧義,總是假設更早的時間。 這樣,當您檢測到同步時間晚於來自 A 的時間戳時,無論如何解釋不明確的同步時間都是如此。 這也意味着您將按照您的建議等到最后一次同步時間在凌晨 2 點之后。 我不認為這在比較方面會非常昂貴,但如果您需要確定,您需要自己進行測量和判斷。

    final DateTimeFormatter timestampFormatter
            = DateTimeFormatter.ofPattern("uuuu-MM-dd-HH-mm-ss");
    final ZoneId systemBTimeZone = ZoneId.of("America/New_York");
    final String syncFormatPattern = "'Sync''ed-as-of time:' EEE MMM d HH:mm";
    final DateTimeFormatter currentSystemBTiemFormatter = new DateTimeFormatterBuilder()
            .appendPattern("EEE MMM dd HH:mm:ss ")
            .appendZoneText(TextStyle.SHORT, Collections.singleton(systemBTimeZone))
            .appendPattern(" yyyy")
            .toFormatter(Locale.US);
    final Period maxSyncAge = Period.ofYears(2);

    String systemATimestampString = "2019-11-03-06-05-55";
    String lastSyncMsg = "Sync'ed-as-of time: Sun Nov 3 01:13";
    String currentSystemBTime = "Sun Nov 03 01:13:07 EDT 2019";
    OffsetDateTime systemATimestamp = LocalDateTime.parse(systemATimestampString, timestampFormatter)
            .atOffset(ZoneOffset.UTC);
    ZonedDateTime maxLastSyncTime
            = ZonedDateTime.parse(currentSystemBTime, currentSystemBTiemFormatter);
    ZonedDateTime minLatSyncTime = maxLastSyncTime.minus(maxSyncAge);
    int candidateYear = maxLastSyncTime.getYear();
    ZonedDateTime syncTime;
    while (true) {
        DateTimeFormatter syncFormatter = new DateTimeFormatterBuilder()
                .appendPattern(syncFormatPattern)
                .parseDefaulting(ChronoField.YEAR, candidateYear)
                .toFormatter(Locale.US);
        try {
            syncTime = LocalDateTime.parse(lastSyncMsg, syncFormatter)
                    .atZone(systemBTimeZone)
                    .withEarlierOffsetAtOverlap();
            if (syncTime.isBefore(minLatSyncTime) || syncTime.isAfter(maxLastSyncTime)) {
                throw new IllegalStateException("Last sync time is out of range");
            }
            break;
        } catch (DateTimeParseException dtpe) {
            // Ignore; try next earlier year
        }
        candidateYear--;
    }
    System.out.println("Last sync time: " + syncTime);
    System.out.println("Timestamp from system A: " + systemATimestamp);
    System.out.println("Is OK? " + syncTime.toOffsetDateTime().isAfter(systemATimestamp));

正如代碼片段所示,它的輸出是:

 Last sync time: 2019-11-03T01:13-04:00[America/New_York] Timestamp from system A: 2019-11-03T06:05:55Z Is OK? false

可以看到它選擇將最后一次同步時間01:13解釋為夏令時,偏移量為-04:00,導致校驗失敗。 時間也可能是標准時間,偏移量為 -05:00,在這種情況下,UTC 等效值為 06:13,並且檢查會成功。 但正如所討論的,為了安全起見,我們更願意等到同步時間在 02:00 之后再次變得明確。

while 循環繼續解析字符串,假設不同的年份,直到遇到星期幾匹配的年份。

在 Matt Johnson 的評論中得到了這個,我認為這是最好的解決方案:

現代 Java 代碼應該使用 java.time 包,它從 Java 8 開始就內置了。 ZoneRules.getValidOffsets 將告訴您應用於特定時區中給定 LocalDateTime 的偏移量。 如果返回了多個偏移量,則您處於不明確的時期。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM