簡體   English   中英

Java 美國日歷的周到日期轉換(非 ISO8601)

[英]Java Week to Date conversion for US calendar (non-ISO8601)

我需要將模式 'YYYY0ww' 字符串轉換為日期 - 基於美國日歷(一周中的最少天數 = 1,一周的第一天 = 星期日)。 示例:2020051、2020052、...

目前我正在使用以下方法來構建適當的格式化程序(使用 java.time 包):

DateTimeFormatter weekFormat = DateTimeFormatterBuilder()
                    .parseDefaulting(ChronoField.DAY_OF_WEEK, WeekFields.SUNDAY_START.getFirstDayOfWeek().getValue())
                    .appendValue(WeekFields.SUNDAY_START.weekBasedYear(), 4)
                    .appendValue(WeekFields.SUNDAY_START.weekOfWeekBasedYear(), 3).parseStrict().toFormatter();

DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyyMMdd").withLocale(Locale.US);

給定周的解決日期必須基於給定周的星期日。

似乎有一個模糊的錯誤,因為以下測試之一失敗:

TestCase.assertEquals("20201213", LocalDate.parse("2020051", weekFormat).format(dateFormat)); // success
TestCase.assertEquals("20201220", LocalDate.parse("2020052", weekFormat).format(dateFormat)); // success
TestCase.assertEquals("20201227", LocalDate.parse("2020053", weekFormat).format(dateFormat)); // fail - parsed date contains year=2020, month=12, day=20 which would be CW 52/2020.
TestCase.assertEquals("20210103", LocalDate.parse("2021001", weekFormat).format(dateFormat)); // success

根據https://www.calendar-365.com/2020-calendar.html在美國日歷中存在 2020 年第 53 周 - 所以“2020053”應該是 20201227。

我已經嘗試在周格式化程序上使用 apply.withResolverStyle(ResolverStyle.LENIENT) (因為我在 JDK 實現中注意到,由於某種原因,嚴格解析有時會鉗制 - 請參閱 WeekFields.ofWeekBasedYear() )。 這使得 CW 53/2020 案例工作,但隨后“2021001”的測試以類似的方式失敗:

TestCase.assertEquals("20201213", LocalDate.parse("2020051", weekFormat).format(dateFormat)); // success
TestCase.assertEquals("20201220", LocalDate.parse("2020052", weekFormat).format(dateFormat)); // success
TestCase.assertEquals("20201227", LocalDate.parse("2020053", weekFormat).format(dateFormat)); // success
TestCase.assertEquals("20210103", LocalDate.parse("2021001", weekFormat).format(dateFormat)); // fail - parsed date contains year=2020, month=12, day=27 which would be CW 53/2020.

我很困惑,讓非 ISO8601 日歷正確的星期到日期轉換是如此棘手 - 我認為這個用例不是很奇特。

感謝反饋為什么我的方法沒有提供正確的結果。

更新:在對此進行了更多思考之后-我想我可能誤以為在某些年份美國日歷中有第 53 個日歷周。 至少鏈接的網站提到了 CW 53。但是根據一年中的第一周是涵蓋 1 月 1 日的那一周(從星期日開始)的規則......可能意味着從來沒有日歷周 53(有些網站得到了錯了)。

我自己作為Ole VV發布這個答案提供了基本分析,但拒絕創建答案(在他的個人資料中解釋) - 所以對 Ole VV 表示敬意

首先,必須清楚哪些年份有美國日歷的第 53 個日歷周。 由於美國日歷的規則尚未明確規定為標准(如 ISO8601),因此最常見的規則似乎是 1 月 1 日定義了第一個日歷周(基於從周日開始到周六結束的周數)。 請注意,在知名搜索引擎上發現的許多公開可用的美國日歷包含不正確的周索引,因為第一個日歷周確定不正確。

我寫了一個小(未完善的)程序來計算周數:

import java.util.HashMap;
import java.util.Map;

import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import junit.framework.TestCase;

public class GenerateCWs_TestCase extends TestCase {

    public void test_generate() {

        int startYear = 2015;
        int EndYear = 2028;

        Map<Integer, LocalDate> cw1StartDates = new HashMap<>();

        DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyyMMdd");

        for (int year = startYear; year <= EndYear + 1; year++) {

            LocalDate firstDayInYear = formatter.parseLocalDate(year + "0101");

            System.out.println("Jan 1st. " + year + " -> day of week: " + firstDayInYear.getDayOfWeek());

            LocalDate sundayOfFirstWeek = firstDayInYear.getDayOfWeek() != 7
                    ? firstDayInYear.minusDays(firstDayInYear.getDayOfWeek() - 1 + 1)
                    : firstDayInYear;

            System.out.println("CW1 starts on: " + formatter.print(sundayOfFirstWeek));

            cw1StartDates.put(year, sundayOfFirstWeek);

        }

        for (int year = startYear; year <= EndYear; year++) {

            LocalDate sundayDateOfWeek = cw1StartDates.get(year);

            int weekIndex = 1;

            System.out.println(String.format("CW %02d/%d - Sunday of Week: %s", weekIndex, year,
                    formatter.print(sundayDateOfWeek)));

            while (sundayDateOfWeek.plusWeeks(1).isBefore(cw1StartDates.get(year + 1))) {

                sundayDateOfWeek = sundayDateOfWeek.plusWeeks(1);
                weekIndex++;

                if (weekIndex <= 2 || weekIndex >= 51)
                    System.out.println(String.format("CW %02d/%d - Sunday of Week: %s", weekIndex, year,
                            formatter.print(sundayDateOfWeek)));

            }
            
            System.out.println();

        }

    }

}

輸出:

CW 01/2015 - Sunday of Week: 20141228
CW 02/2015 - Sunday of Week: 20150104
CW 51/2015 - Sunday of Week: 20151213
CW 52/2015 - Sunday of Week: 20151220
CW 01/2016 - Sunday of Week: 20151227
CW 02/2016 - Sunday of Week: 20160103
CW 51/2016 - Sunday of Week: 20161211
CW 52/2016 - Sunday of Week: 20161218
CW 53/2016 - Sunday of Week: 20161225
CW 01/2017 - Sunday of Week: 20170101
CW 02/2017 - Sunday of Week: 20170108
CW 51/2017 - Sunday of Week: 20171217
CW 52/2017 - Sunday of Week: 20171224
CW 01/2018 - Sunday of Week: 20171231
CW 02/2018 - Sunday of Week: 20180107
CW 51/2018 - Sunday of Week: 20181216
CW 52/2018 - Sunday of Week: 20181223
CW 01/2019 - Sunday of Week: 20181230
CW 02/2019 - Sunday of Week: 20190106
CW 51/2019 - Sunday of Week: 20191215
CW 52/2019 - Sunday of Week: 20191222
CW 01/2020 - Sunday of Week: 20191229
CW 02/2020 - Sunday of Week: 20200105
CW 51/2020 - Sunday of Week: 20201213
CW 52/2020 - Sunday of Week: 20201220
CW 01/2021 - Sunday of Week: 20201227
CW 02/2021 - Sunday of Week: 20210103
CW 51/2021 - Sunday of Week: 20211212
CW 52/2021 - Sunday of Week: 20211219
CW 01/2022 - Sunday of Week: 20211226
CW 02/2022 - Sunday of Week: 20220102
CW 51/2022 - Sunday of Week: 20221211
CW 52/2022 - Sunday of Week: 20221218
CW 53/2022 - Sunday of Week: 20221225
CW 01/2023 - Sunday of Week: 20230101
CW 02/2023 - Sunday of Week: 20230108
CW 51/2023 - Sunday of Week: 20231217
CW 52/2023 - Sunday of Week: 20231224
CW 01/2024 - Sunday of Week: 20231231
CW 02/2024 - Sunday of Week: 20240107
CW 51/2024 - Sunday of Week: 20241215
CW 52/2024 - Sunday of Week: 20241222
CW 01/2025 - Sunday of Week: 20241229
CW 02/2025 - Sunday of Week: 20250105
CW 51/2025 - Sunday of Week: 20251214
CW 52/2025 - Sunday of Week: 20251221
CW 01/2026 - Sunday of Week: 20251228
CW 02/2026 - Sunday of Week: 20260104
CW 51/2026 - Sunday of Week: 20261213
CW 52/2026 - Sunday of Week: 20261220
CW 01/2027 - Sunday of Week: 20261227
CW 02/2027 - Sunday of Week: 20270103
CW 51/2027 - Sunday of Week: 20271212
CW 52/2027 - Sunday of Week: 20271219
CW 01/2028 - Sunday of Week: 20271226
CW 02/2028 - Sunday of Week: 20280102
CW 51/2028 - Sunday of Week: 20281210
CW 52/2028 - Sunday of Week: 20281217
CW 53/2028 - Sunday of Week: 20281224

因此,2022 年在美國日歷中有第 53 個日歷周。 2020 年沒有第 53 個日歷周。以下部分基於嘗試計算 2020 年第 53 個日歷周的星期日。

從這里適用於評論中 Ole VV 的深入分析,它描述了 DateTimeFormatter 在 java.time package 中非常意外的行為:在給定一周的第一天解析 2020 年 CW53 的年+周信息(周日)結果是 2020/12/20,但是是日歷周 52(沒有任何異常情況的跡象)。 沒有意識到這一點的程序會默默地繼續處理從第 52 周返回的日歷日期,盡管輸入的是第 53 周日歷。

在這種情況下,使用 ResolverStyle.STRICT沒有任何區別。

唯一可能的保護是計算給定年+周的日歷日期,然后通過反轉計算添加額外的檢查。 如果輸入周和 output 周不匹配,則發生所述錯誤行為,需要在客戶端代碼中進行特殊處理。

Ole 好心報告了一個錯誤: JDK-8293146: Strict DateTimeFormatter failed to report an invalid week 53 in the Oracle Java 錯誤數據庫

暫無
暫無

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

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