DateTimeFormatter工作日似乎是一个人

Nie*_*jes 14 jodatime dayofweek datetime-parsing java-8 java-time

我正在将现有的应用程序从Joda-Time移植到Java 8 java.time.

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

解析时:

2016-12-21 20:50:25 12月周三+0000 3

使用格式:

yyyy'-'MM'-'dd' 'HH':'mm':'ss' 'EEEE' 'MMMM' 'ZZ' 'e
Run Code Online (Sandbox Code Playgroud)

我明白了:

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
Run Code Online (Sandbox Code Playgroud)

当让它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));
Run Code Online (Sandbox Code Playgroud)

我现在得到这个输出:

2016-12-21 20:50:25 Wednesday December +0000 4
Run Code Online (Sandbox Code Playgroud)

因此,问题的根本原因是,eJoda-Time 中的标志认为周一是1Java 8 java.time认为的星期一0.

现在,对于java.time.DateTimeFormatter支持的模式,我在Oracle文档和JSR-310 中都可以找到:

e/c     localized day-of-week       number/text       2; 02; Tue; Tuesday; T
Run Code Online (Sandbox Code Playgroud)

这个明确的例子2和'星期二'让我相信星期三也应该在java.time 3而不是4.

这有什么不对?我误解了吗?这是Java 8中的错误吗?

小智 11

Joda-Time如何java.time解释模式有所不同e.


在Joda-Time中,e模式指定星期几的数值:

Symbol  Meaning        Presentation  Examples
------  -----------    ------------  -------
e       day of week    number        2
Run Code Online (Sandbox Code Playgroud)

因此,使用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
Run Code Online (Sandbox Code Playgroud)

注意formatter和getDayOfWeek()return 3.该getDayOfWeek()方法返回在DateTimeConstants类中定义的值3,并且星期三的值是(根据ISO的定义,一周的第三天).


java.timeAPI中,模式e 具有不同的含义:

Pattern  Count  Equivalent builder methods
-------  -----  --------------------------
e        1      append special localized WeekFields element for numeric day-of-week
Run Code Online (Sandbox Code Playgroud)

它使用了本地化WeekFields元素,这可能会根据区域设置而有所不同.与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
Run Code Online (Sandbox Code Playgroud)

请注意,格式化程序使用英语区域设置的本地化星期几,并且4在调用getDayOfWeek().getValue()返回时使用值3.

那是因为e英语语言环境相当于使用java.time.temporal.WeekFields:

// using localized fields
WeekFields wf = WeekFields.of(Locale.ENGLISH);
System.out.println(z.get(wf.dayOfWeek())); // 4
Run Code Online (Sandbox Code Playgroud)

虽然getDayOfWeek()相当于使用ISO的定义:

// same as getDayOfWeek()
System.out.println(z.get(WeekFields.ISO.dayOfWeek())); // 3
Run Code Online (Sandbox Code Playgroud)

这是因为ISO的定义使用星期一作为一周的第一天,而WeekFields英语语言环境使用星期日:

// comparing the first day of week
System.out.println(WeekFields.ISO.getFirstDayOfWeek()); // MONDAY
System.out.println(wf.getFirstDayOfWeek()); // SUNDAY
Run Code Online (Sandbox Code Playgroud)

因此,根据格式化程序中设置的区域设置(或JVM默认区域设置,如果没有设置),e模式可能表现不同或不相同getDayOfWeek().例如,在法语区域设置中,它的行为与ISO类似,而在某些阿拉伯语区域设置中,一周的第一天是星期六:

WeekFields.of(Locale.FRENCH).getFirstDayOfWeek(); // MONDAY
WeekFields.of(new Locale("ar", "AE")).getFirstDayOfWeek(); // SATURDAY
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)

还要注意的是,你不需要引用-,:和空格字符,这样的格局变得更加清晰易读(IMO).

我还设置了英语语言环境,因为如果你没有设置,它将使用JVM默认语言环境,并且不能保证始终是英语.即使在运行时也可以在不事先通知的情况下进行更改,因此最好指定一个,特别是如果您已经知道输入的语言是什么.


更新:可能该ccccc模式应该工作,因为它等同appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW_STANDALONE)于我的测试(JDK 1.8.0_144)它返回(并且还解析)3:

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

  • @NielsBasjes欢迎您,很乐意提供帮助!我做了一些测试,也许模式`ccccc`应该是你需要的.我已经更新了答案. (2认同)