【问题标题】:LocalDate cannot parse 'ww' with 'yyyy'LocalDate 无法用 'yyyy' 解析 'ww'
【发布时间】:2017-08-16 03:24:25
【问题描述】:

我必须使用以下格式解析日期:“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(一周的第一天必须是星期日)。

【问题讨论】:

  • 您的代码和您的异常不匹配。代码显示“201710”,但异常显示“2017101”。也许是错字?
  • 只是错字,异常是 java.time.format.DateTimeParseException: Text '201710' could not be parsed at index 0 我看到“yyyyw”格式不支持。有什么问题?
  • 新的java.time 类似乎和旧的一样用户友好。
  • @Kayaman,请帮忙设计模式。
  • @Kayaman,感谢您指出另一个问题。当年和周之间有空格时,那里的答案解决了这个问题,而不是当年和周是一串 6 位数字,没有什么可分开的。

标签: java datetime-format localdate


【解决方案1】:

@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 中找到这样的类型。示例:

    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

【讨论】:

    【解决方案2】:

    如果值之间有空格,则(以前的)duplicate 可以工作,但是如果没有空格,则下面的解析很好。

    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)。

    【讨论】:

    • @Will Graham,请检查您是否想要一年和一年中的一周对齐。这是一致的和不寻常的。您可能需要基于周的年份和周数。这是一个很好的答案,因为它为您提供了工作代码,然后您可以根据需要进行定制。
    • 我在回答中给出了一个反例。
    【解决方案3】:

    ThreeTen-Extra

    ThreeTen-Extra 项目为 java.time 类添加了功能。

    YearWeek

    该库提供了可能对您有用的YearWeek 类。这个类使用ISO 8601标准definition of a week

    如果可能,我建议你将自己的字符串更改为使用标准格式:yyyy-Www 例如2018-W07YearWeek 类中默认使用标准格式来生成/解析字符串。

    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 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

    Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

    要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

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

    从哪里获得 java.time 类?

    ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

    【讨论】:

      猜你喜欢
      • 2018-03-01
      • 1970-01-01
      • 2017-11-12
      • 1970-01-01
      • 2014-10-16
      • 2015-10-14
      • 2016-04-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多