如何使用 java.time 从带有年份和星期的字符串中解析日期

How to parse date from string with year and week using java.time

旧的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.time.format.Parsed

的 ISO

如何在java.time中完成?

此外,我不满意我必须通过区域设置来确定一周的第一天:星期一与星期日。这不是国家特征而是日历特征。我想使用 java.time.temporal.WeekFields.ISO 之类的东西向世界展示从星期一开始的一周

我发现了类似的案例:

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

直接回答和解决:

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 周行为,如我在本答案的第一个版本中所示(原因对我来说还不清楚 - 似乎有必要仔细查看来源以启发不直观的行为)。

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

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

是正确的。这是另一条路线。

YearWeek

使用 YearWeek class in ThreeTen-Extra library. This library extends java.time with additional functionality. This particular class represents a year-week in the ISO 8601 week date system.

工厂方法YearWeek.of接受一对整数参数,week-based-year和周数。

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

ISO 8601

理想情况下,您将使用标准 ISO 8601 formats for strings representing date-time values. For year-week that would be yyyy-Www、week-based-year 数字、一个连字符、一个 W 和两位数的周数,并根据需要填充零。

2015-W01

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

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

正在解析整数。

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

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

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

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

现在调用那个工厂方法。

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

LocalDate

关于一周的第一天,这里我们一直在讨论标准的ISO 8601对周的定义。在该定义中,星期一始终是第一天,即从 Monday-Sunday 开始的一周 运行。第 1 周包含 calendar-year 的第一个星期四。在 2015-W01 的情况下,那个星期四是 2015 年 1 月 1 日。

所以,不需要 Locale

我不太确定你的目标,但它似乎是为你一周中的几天提取特定日期。使用 YearWeek class 和 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.

这也可以通过使用 "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()));