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

如何使用java.time从带有年和周的字符串中解析日期,java,java-time,Java,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中,我可以这样做:

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
我想在Java8中使用java.time

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

java.time.format.DateTimeParseException:无法分析文本“2015 1”:无法从临时Accessor获取LocalDate:{WeekOfWeekBasedYear[WeekFields[SUNDAY,1]]=1,Year=2015},类型为java.time.format.parsed的ISO

如何在java.time中实现这一点

此外,我不满意我必须通过地区来确定一周的第一天:周一对周日。这不是国家功能,而是日历功能。我想用java.time.temporal.WeekFields.ISO这样的东西向世界展示从周一开始的那一周

我发现了类似的情况:


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

直接回答和解决方案:

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

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感兴趣,而不是纪年

b) 日历日期不能仅通过给出(基于周的)年和周数来形成。一周中的哪一天对确定指定日历周内的哪一天很重要。预定义的需要缺少一周中的某一天。因此,您需要使用生成器模式构造一个专门的解析器。然后有必要通过方法
parseDefaulting()
告诉解析器需要一周中的哪一天

c) 我坚持(并在这里为JSR-310辩护)说,一周开始的问题不是日历问题,而是国家相关问题。美国和法国(例如)使用相同的日历,但对如何定义一周有不同的看法。ISO-8601-标准可以使用明确的ISO引用字段
WeekFields.ISO.dayOfWeek()
应用注意:测试表明,使用
ChronoField.DAY\u OF\u WEEK
Locale.ROOT
似乎并不总能保证ISO WEEK行为,如我在本答案的第一个版本中所述(原因对我来说还不清楚——近距离观察来源似乎是必要的,以启发非直觉行为)

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

e) 旁白:
SimpleDateFormat
默认情况下行为温和。java时间包更为严格,拒绝凭空创造一周中缺少的一天——即使是在宽松的模式下(我认为这是一件好事)。软件不应该猜测太多,相反,程序员应该更多地考虑一周中哪一天是正确的。这里再次说明:在正确的默认设置方面,美国和法国的应用程序要求可能会有所不同。

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

YearWeek
使用库中的类。该库通过附加功能扩展了java.time。这个特殊的班级代表一年中的一周

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

YearWeek yw = YearWeek.of( 2015 , 1 );  
ISO 8601 理想情况下,对于表示日期时间值的字符串,应该使用标准格式。对于基于周的年号,使用连字符、a
W
和两位数字表示周号,并根据需要填充零

2015-W01

在解析和生成字符串时,java.time类和三个额外的类默认使用标准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标准对一周的定义。按照这个定义,周一永远是第一天,从周一到周日的一周。第一周包含日历年的第一个星期四。对于2015-W01,该周四为2015年1月1日

因此,不需要
Locale

我不太确定你的目标,但它似乎提取了你一周中几天的特定日期。对于
YearWeek
类和enum,这非常容易


这也可以通过使用“ChronoField.Day\u 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()));

我的意思是,当我想使用自定义的星期一/星期日设置时,我不应该使用我想要的星期天定义的区域设置,而是明确定义一周的第一天。建议的答案(使用Locale.ROOT)是我要求的100%:我将解决方案应用于2016年:System.out.println(LocalDate.parse(“2016 1”,new DateTimeFormatterBuilder().appendPattern(“YYYY w”).parseDefaulting(ChronoField.DAY\u OF_WEEK,1)。toFormatter().withLocale(Locale.ROOT))); // 输出:2015-12-28为什么我得到2015-12-28?我希望得到2016-01-04,因为2016年的第一周是4-10January@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,这是正确的。所以我无法重现你所观察到的。