Java 8 DateTimeFormatter工作日似乎缩短了一天

Java 8 DateTimeFormatter工作日似乎缩短了一天,java-8,jodatime,java-time,dayofweek,datetime-parsing,Java 8,Jodatime,Java Time,Dayofweek,Datetime Parsing,我正在将一个现有的应用程序从JodaTime移植到Java8Java.Time 我遇到了一个问题,解析包含“星期几”值的日期/时间字符串在我的单元测试中触发了一个异常 解析时: 2016-12-21 20:50:25 12月23日星期三+0000 3 使用格式: yyyy'-'MM'-'dd' 'HH':'mm':'ss' 'EEEE' 'MMMM' 'ZZ' 'e 我得到: java.time.format.DateTimeParseException: Text '2016-12-21

我正在将一个现有的应用程序从JodaTime移植到Java8
Java.Time

我遇到了一个问题,解析包含“星期几”值的日期/时间字符串在我的单元测试中触发了一个异常

解析时:

2016-12-21 20:50:25 12月23日星期三+0000 3

使用格式:

yyyy'-'MM'-'dd' 'HH':'mm':'ss' 'EEEE' 'MMMM' 'ZZ' 'e
我得到:

java.time.format.DateTimeParseException: 
Text '2016-12-21 20:50:25 Wednesday December +0000 3' 
could not be parsed: Conflict found: 
Field DayOfWeek 3 differs from DayOfWeek 2 derived from 2016-12-21
当让
DateTimeFormatter
指示其期望值时:

String logline     = "2016-12-21 20:50:25 Wednesday December +0000";
String format      = "yyyy'-'MM'-'dd' 'HH':'mm':'ss' 'EEEE' 'MMMM' 'ZZ";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format).withLocale(Locale.ENGLISH);;
ZonedDateTime dateTime = formatter.parse(logline, ZonedDateTime::from);

format      = "yyyy'-'MM'-'dd' 'HH':'mm':'ss' 'EEEE' 'MMMM' 'ZZ' 'e";
formatter = DateTimeFormatter.ofPattern(format).withLocale(Locale.ENGLISH);
System.out.println(formatter.format(dateTime));
我现在得到这个输出:

2016-12-21 20:50:25 Wednesday December +0000 4
因此,问题的根本原因实际上是Joda Time中的
e
标志将周一视为
1
,而Java 8
Java.Time
将周一视为
0

现在,对于
java.time.DateTimeFormatter
支持的模式,我在JSR-310和JSR-310中都找到了以下内容:

e/c     localized day-of-week       number/text       2; 02; Tue; Tuesday; T
这个关于
2
和“星期二”的明确例子让我相信,在java.time中,星期三也应该是
3
而不是
4

这里怎么了? 我误解了吗?
这在Java 8中是一个bug吗?

语言环境中。英语
周三是一周的第四天,因为一周从周日开始。 您可以在每周的第一天与

WeekFields.of(Locale.ENGLISH).getFirstDayOfWeek(); //it's SUNDAY

Joda Time和
java.Time
解释模式
e
的方式不同


在Joda时间内,
e
模式:

因此,使用
e
相当于从日期对象获取一周中的某一天:

// using org.joda.time.DateTime and org.joda.time.format.DateTimeFormat
DateTime d = new DateTime(2016, 12, 21, 20, 50, 25, 0, DateTimeZone.UTC);
DateTimeFormatter fmt = DateTimeFormat.forPattern("e").withLocale(Locale.ENGLISH);
System.out.println(d.toString(fmt)); // 3
System.out.println(d.getDayOfWeek()); // 3
System.out.println(d.dayOfWeek().getAsText(Locale.ENGLISH)); // Wednesday
请注意,格式化程序和
getDayOfWeek()
都返回
3
。返回在和中定义的值(根据)


java.time
API中,模式
e

它使用本地化的元素,这可能会根据区域设置而有所不同。与
getDayOfWeek()
方法相比,行为可能有所不同:

ZonedDateTime z = ZonedDateTime.of(2016, 12, 21, 20, 50, 25, 0, ZoneOffset.UTC);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("e", Locale.ENGLISH);
System.out.println(z.format(fmt)); // 4
System.out.println(z.getDayOfWeek()); // WEDNESDAY
System.out.println(z.getDayOfWeek().getValue()); // 3
请注意,格式化程序在调用
getDayOfWeek()。getValue()
返回
3
时,使用英语区域设置的本地化星期几,值为
4

这是因为英语区域设置的
e
相当于使用
java.time.temporal.WeekFields

// using localized fields
WeekFields wf = WeekFields.of(Locale.ENGLISH);
System.out.println(z.get(wf.dayOfWeek())); // 4
getDayOfWeek()
相当于使用ISO的定义:

// same as getDayOfWeek()
System.out.println(z.get(WeekFields.ISO.dayOfWeek())); // 3
这是因为ISO的定义使用星期一作为一周的第一天,而英语地区的
WeekFields
使用星期天:

// comparing the first day of week
System.out.println(WeekFields.ISO.getFirstDayOfWeek()); // MONDAY
System.out.println(wf.getFirstDayOfWeek()); // SUNDAY
因此,
e
模式的行为可能与
getDayOfWeek()
不同,这取决于格式化程序中设置的区域设置(或者JVM默认区域设置,如果未设置)。例如,在法语地区,它的行为与ISO类似,而在某些阿拉伯语地区,一周的第一天是星期六:

WeekFields.of(Locale.FRENCH).getFirstDayOfWeek(); // MONDAY
WeekFields.of(new Locale("ar", "AE")).getFirstDayOfWeek(); // SATURDAY

根据,返回星期几数值的唯一模式似乎是本地化模式。因此,要解析输入
2016-12-21 20:50:25星期三12月+0000 3
,您可以使用
java.time.format.DateTimeFormatterBuilder
,并将日期/时间模式与
java.time.temporal.ChronoField
连接,以指示星期几的数值(ISO非地区敏感字段):

还请注意,您不需要引用
-
和空格字符,因此模式变得更清晰易读(IMO)

我还设置了英语区域设置,因为如果不设置,它将使用JVM默认区域设置,并且不能保证总是英语。而且,即使在运行时,也可以在不通知的情况下对其进行更改,因此最好指定一个,特别是如果您已经知道输入的语言是什么的话


更新:可能
CCCCCCC
模式应该可以工作,因为它相当于
appendText(ChronoField.DAY\u OF\u WEEK,TextStyle.slown\u STANDALONE)
,在我的测试(JDK 1.8.0\u 144)中它返回(也解析)
3

DateTimeFormatter parser = DateTimeFormatter
    .ofPattern("yyyy-MM-dd HH:mm:ss EEEE MMMM ZZ ccccc", Locale.ENGLISH);
ZonedDateTime date = ZonedDateTime.parse(input, parser);

您使用的时区/地区是什么,正如文档所说,这是一周中本地化的一天,一些地区从周日开始,而不是周一。为了澄清,我添加了.withLocale(locale.ENGLISH);在密码里,谢谢。这个解释真的很有帮助。我引用了所有这些字段,因为表达式是从strftime格式的输入字符串生成的。我必须找出一种方法来附加这些值,使其正常工作。看@NielsBasjes不客气,很乐意帮忙!我做了一些测试,也许模式
ccccccc
应该是您所需要的。我已经更新了答案。
WeekFields.of(Locale.FRENCH).getFirstDayOfWeek(); // MONDAY
WeekFields.of(new Locale("ar", "AE")).getFirstDayOfWeek(); // SATURDAY
String input = "2016-12-21 20:50:25 Wednesday December +0000 3";
DateTimeFormatter parser = new DateTimeFormatterBuilder()
    // date/time pattern
    .appendPattern("yyyy-MM-dd HH:mm:ss EEEE MMMM ZZ ")
    // numeric day of week
    .appendValue(ChronoField.DAY_OF_WEEK)
    // create formatter with English locale
    .toFormatter(Locale.ENGLISH);

ZonedDateTime date = ZonedDateTime.parse(input, parser);
DateTimeFormatter parser = DateTimeFormatter
    .ofPattern("yyyy-MM-dd HH:mm:ss EEEE MMMM ZZ ccccc", Locale.ENGLISH);
ZonedDateTime date = ZonedDateTime.parse(input, parser);