繁体   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