【问题标题】:How to parse date from string with year and week using java.time如何使用java.time从带有年份和星期的字符串中解析日期
【发布时间】:2021-09-06 13:06:07
【问题描述】:

在旧的 java 中我可以这样做:

System.out.println(new SimpleDateFormat("yyyy w", Locale.UK).parse("2015 1"));
// shows Mon Dec 29 00:00:00 CET 2014

System.out.println(new SimpleDateFormat("yyyy w", Locale.US).parse("2015 1"));
// shows Mon Dec 28 00:00:00 CET 2014

我想在 Java 8 中使用 java.time。

System.out.println( LocalDate.parse("2015 1", DateTimeFormatter.ofPattern("yyyy w", Locale.US)));

结果:

java.time.format.DateTimeParseException:无法解析文本“2015 1”:无法从 TemporalAccessor 获取 LocalDate:{WeekOfWeekBasedYear[WeekFields[SUNDAY,1]]=1,Year=2015},java 类型的 ISO .time.format.Parsed

在java.time中怎么做?

此外,我不满意我必须通过语言环境来确定一周的第一天:周一与周日。它不是国家/地区功能,而是日历功能。我想使用 java.time.temporal.WeekFields.ISO 之类的东西来向世界展示从星期一开始的那一周

我发现了类似的案例:https://stackoverflow.com/questions/3941700/how-to-get-dates-of-a-week-i-know-week-number

但不适用于 Java 8 中的 java.time。此外,首先创建日期对象然后设置正确星期的解决方案并不优雅。我想一次性创建最终日期。

【问题讨论】:

    标签: java java-time


    【解决方案1】:

    直接回答和解决方案:

    System.out.println( 
      LocalDate.parse("2015 1", 
        new DateTimeFormatterBuilder().appendPattern("YYYY w")
        .parseDefaulting(WeekFields.ISO.dayOfWeek(), 1)
        .toFormatter()));
    // output: 2014-12-29
    

    说明:

    a) 您应该使用 Y 而不是 y,因为您对 ISO-8601-week-date 感兴趣,而不是 year-of-era。

    b) 不能仅通过给出(基于周的)年份和周数来形成日历日期。星期几很重要,以确定指定日历周内的日期。预定义的 formatter for week-dates 需要缺少的星期几。因此,您需要使用构建器模式构建一个专门的解析器。然后有必要告诉解析器需要星期几 - 通过方法parseDefaulting()

    c) 我坚持(并在此为 JSR-310 辩护)说一周开始的问题不是日历问题,而是取决于国家/地区的问题。美国和法国(例如)使用相同的日历,但对如何定义一周有不同的看法。可以使用明确的 ISO 引用字段 WeekFields.ISO.dayOfWeek() 应用 ISO-8601 标准。 注意: 测试表明,使用 ChronoField.DAY_OF_WEEKLocale.ROOT 似乎并不总是能保证 ISO-week-behaviour 正如我在这个答案的第一个版本中所指出的那样(原因尚不清楚我 - 似乎有必要仔细查看来源以启发不直观的行为)。

    d) java-time-package 做得很好 - 除了星期一被指定为数字 1。我更喜欢枚举。或者使用枚举及其方法getValue()

    e) 旁注:SimpleDateFormat 默认表现宽松。 java-time-package 更加严格,并且拒绝凭空发明缺少的星期几 - 即使在宽松模式下(在我看来这是一件好事)。软件不应该猜测太多,相反,程序员应该更多地考虑星期几是正确的。同样在这里:美国和法国的应用程序要求可能会在正确的默认设置方面有所不同。

    【讨论】:

    • Ad c) 我不准确。我的意思是,当我想使用自定义星期一/星期日设置时,我不应该使用按我想要定义工作日但明确定义第一天的语言环境周。建议的答案(使用 Locale.ROOT)是我所问的 100%
    • 我将解决方案应用于 2016 年: System.out.println( LocalDate.parse("2016 1", new DateTimeFormatterBuilder().appendPattern("YYYY w") .parseDefaulting(ChronoField.DAY_OF_WEEK, 1) .toFormatter().withLocale(Locale.ROOT))); // 输出:2015-12-28 为什么我得到 2015-12-28?我希望得到 2016-01-04,因为 2016 年的第一周是 1 月 4 日至 10 日
    • @michaldo 非常感谢您的反馈。我改变了我的答案,更喜欢第二种解决方案。这仍然适用于2016-1,产生结果2016-01-04。似乎我们在 JSR-310-API 中遇到了一些奇怪的问题。
    • 我尝试将此代码用于:“2015 52”和“2015 53”,但结果不正确。两种情况都返回相同的日期:2015-12-21。
    • @DaveFord 对我来说,上面的代码在第 52 周生成日期为 2015-12-21,在第 53 周生成日期为 2015-12-28,这是正确的。所以我无法重现您观察到的内容。
    【解决方案2】:

    accepted Answer by Meno Hochschild 是正确的。这是一条替代路线。

    YearWeek

    使用ThreeTen-Extra 库中的YearWeek 类。这个库扩展了 java.time 的附加功能。这个特定的类代表ISO 8601 week date system 中的一年一周。

    工厂方法YearWeek.of 采用一对整数参数,即基于周的年份和周数。

    YearWeek yw = YearWeek.of( 2015 , 1 );  
    

    ISO 8601

    理想情况下,您将使用标准ISO 8601 格式来表示日期时间值的字符串。对于year-week that would be yyyy-Www,基于周的年份编号、连字符、W 和两位数字表示周编号,并根据需要填充零。

    2015-W01

    java.time 类和 ThreeTen-Extra 类在解析和生成字符串时默认使用标准 ISO 8601 格式。因此,如果您坚持标准,生活会轻松得多

    YearWeek yw = YearWeek.parse( "2015-W01" );
    String output = yw.toString(); // 2015-W01
    

    解析整数。

    您有一个非标准的数字格式。因此,让我们将您的字符串解析为两部分,每部分一个数字,以解释为int 整数。

    String string = "2015 1";
    String[] parts = string.split(" "); // SPACE character.
    String part1 = parts[0]; // 2015
    String part2 = parts[1]; // 1
    

    Integer 类将此类字符串转换为 int 原始值。

    int weekBasedYearNumber = Integer.parseInt( part1 ) ;
    int weekNumber = Integer.parseInt( part2 ) ;
    

    现在调用那个工厂方法。

    YearWeek yw = YearWeek.of( weekBasedYearNumber , weekNumber );
    

    LocalDate

    至于一周的第一天,我们在这里一直在讨论标准 ISO 8601 对周的定义。在该定义中,星期一始终是第一天,从星期一到星期日连续一周。第 1 周包含日历年的第一个星期四。在 2015-W01 的情况下,星期四是 2015 年 1 月 1 日。

    所以,不需要Locale

    我不太确定您的目标,但它似乎是在提取您一周内几天的特定日期。使用 YearWeek 类和 DayOfWeek 枚举非常容易。

    LocalDate monday = yw.atDay( DayOfWeek.MONDAY );  // 2014-12-29 start-of-week.
    LocalDate friday = yw.atDay( DayOfWeek.FRIDAY );  // 2015-01-02
    LocalDate sunday = yw.atDay( DayOfWeek.SUNDAY );  // 2015-01-04 end-of-week.
    

    【讨论】:

      【解决方案3】:

      这也可以通过使用“ChronoField.Day_Of_Week”为星期几设置默认解析值并将值设置为“1”来实现,如下所示:

      System.out.println( "Date From Year and Week : "+
                    LocalDate.parse("2015 1", 
                      new DateTimeFormatterBuilder().appendPattern("YYYY w")
                      .parseDefaulting(ChronoField.DAY_OF_WEEK, 1)
                      .toFormatter()));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-04
        • 1970-01-01
        • 1970-01-01
        • 2012-05-09
        • 2020-12-19
        相关资源
        最近更新 更多