如何使用 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_WEEK
与 Locale.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()));
旧的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.timeSystem.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_WEEK
与 Locale.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()));