LocalDate 无法用 'yyyy' 解析 'ww'

LocalDate cannot parse 'ww' with 'yyyy'

我必须使用以下格式解析日期:“201710”,其中 10 - 一年中的第几周。我尝试以这种方式实现它:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyw");
java.time.LocalDate startDate = java.time.LocalDate.parse("201710", formatter);
System.out.println(startDate);

但它抛出异常:

java.time.format.DateTimeParseException: Text '201710' could not be parsed at index 0   

然后我需要从 LocalDate 对象获取一周的第一天和最后一天。 例如“201710”- 05.03 12.03(一周的第一天必须是星期日)。

如果值之间有 space,则(上一个) 有效,但是如果没有 space,则以下解析很好。

DateTimeFormatter dtf = new DateTimeFormatterBuilder()
        .appendValue(ChronoField.YEAR, 4)
        .appendValue(ChronoField.ALIGNED_WEEK_OF_YEAR, 2)
        .parseDefaulting(WeekFields.SUNDAY_START.dayOfWeek(), 1)
        .toFormatter();

System.out.println(LocalDate.parse("201710", dtf));
// 2017-03-05

SUNDAY_START 替换为 ISO 将为您提供从星期一开始的几周(因此它将打印 2017-03-06)。

@Kayaman 接受的答案不正确,因为您不能混合使用标准日期表示(使用 yyyy = 年份)和星期日期表示(使用 ww = 基于星期的年份)。 标准日历年和基于星期的年份之间的细微差别与日历年的开始或结束有关。结论:不要使用符号 "y",而是符号 "Y"。输入“201501”的反例:

正确解

DateTimeFormatter formatter =
    new DateTimeFormatterBuilder()
    .appendValue(WeekFields.ISO.weekBasedYear(), 4)
    .appendValue(WeekFields.ISO.weekOfWeekBasedYear(), 2)
    .parseDefaulting(ChronoField.DAY_OF_WEEK, 1)
    .toFormatter();
LocalDate startDate = LocalDate.parse("201501", formatter);
System.out.println(startDate); // 2014-12-29

根据@Kayaman的提议:

DateTimeFormatter dtf =
    new DateTimeFormatterBuilder()
    .appendValue(ChronoField.YEAR, 4)
    .appendValue(ChronoField.ALIGNED_WEEK_OF_YEAR, 2)
    .parseDefaulting(WeekFields.ISO.dayOfWeek(), 1)
    .toFormatter();
System.out.println(LocalDate.parse("201501", dtf)); // 2015-01-05 (wrong)

结果日期不同!差异是由日历年的定义引起的,日历年总是从一月一日开始,而基于周的年份总是从星期一开始(ISO-8601 定义),使用日历年的第一周至少有 4 天。

附加说明 a): Java-8 不管理可本地化字段的相邻数字解析,例如基于周的字段(另请参见相关的 JDK issue),因此我选择了基于构建器的解决方案,而不是定义模式 "YYYYww"(但是,Java-9 承诺了一个解决方案)。但即使使用 Java-9,基于构建的方法仍然是必要的,因为需要为缺少的星期几定义默认值(此处:设置为星期一)。

附加说明 b): 如果您正在寻找基于周的年份和年份的组合的真实类型并使用 LocalDate作为这种缺失类型的解决方法,您可以在 Threeten-Extra 或我的库 Time4J 中的 3rd 方库中找到这样的类型。示例:

    ChronoFormatter<CalendarWeek> cf =
        ChronoFormatter.ofPattern(
            "YYYYww",
            PatternType.CLDR,
            Locale.ROOT,
            CalendarWeek.chronology()
        );
    CalendarWeek cw = cf.parse("201501");
    System.out.println(cw); // 2015-W01
    System.out.println(cw.at(Weekday.MONDAY)); // 2014-12-29

三十加

ThreeTen-Extra 项目向 java.time classes 添加了功能。

YearWeek

该库提供了可能对您有用的 YearWeek class。此 class 使用 ISO 8601 standard definition of a week.

如果可能,我建议您将自己的字符串更改为使用标准格式:yyyy-Www 例如2018-W07。默认情况下,YearWeek class 中的 generating/parsing 字符串使用标准格式。

YearWeek yw = YearWeek.parse( "2018-W07" );

但是如果您坚持自己的非标准格式,我们必须定义我们自己的 DateTimeFormatter 来匹配您的输入。我从 YearWeek.java source code 中提取了一行 YearWeek.parse 方法,并通过禁用两个方法调用进行了修改以适合您的情况。

DateTimeFormatter f =
new DateTimeFormatterBuilder()
// .parseCaseInsensitive()
.appendValue( WEEK_BASED_YEAR , 4 , 10 , SignStyle.EXCEEDS_PAD )
// .appendLiteral("-W")
.appendValue( WEEK_OF_WEEK_BASED_YEAR , 2 )
.toFormatter();

我们来试试吧。

YearWeek yw = YearWeek.parse( "201807" , f );

yw.toString(): 2018-W07


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

您可以直接与数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes.

从哪里获得java.time classes?

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.