我正在将现有的应用程序从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
我明白了:
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
所以问题的根本原因就是这个问题 e Joda-Time的旗帜周一认为 1 但是Java 8 java.time 考虑周一 0。
现在的模式 java.time.DateTimeFormatter 支持我在两者中找到 Oracle文档 在JSR-310中:
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
这个明确的例子 2 并且'星期二'让我相信周三也应该在java.time中 3 代替 4。
这有什么不对?
我误解了吗?
这是Java 8中的错误吗?
Joda-Time和Joda-Time有什么不同 java.time 解释模式 e。
在Joda-Time, e 模式 指定星期几的数值:
Symbol Meaning Presentation Examples
------ ----------- ------------ -------
e day of week number 2
所以,使用 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。该 getDayOfWeek() 方法 返回一个定义的值 DateTimeConstants 类,和 星期三的价值是 3 (一周中的第三天 根据 ISO的定义)。
在 java.time API,模式 e 有不同的含义:
Pattern Count Equivalent builder methods
------- ----- --------------------------
e 1 append special localized WeekFields element for numeric day-of-week
它使用本地化 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
请注意,格式化程序使用英语区域设置的本地化星期几,值为 4在打电话的时候 getDayOfWeek().getValue() 回报 3。
那是因为 e 英语语言环境相当于使用a 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
根据 的javadoc,返回一周中数字值的唯一模式似乎是本地化的。所以,要解析输入 2016-12-21 20:50:25 Wednesday December +0000 3,你可以使用 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);
另请注意,您无需引用 -, : 和空格字符,因此模式变得更加清晰和可读(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);
在 Locale.ENGLISH 星期三是星期四的第四天,星期日开始。
你可以查看一周的第一天
WeekFields.of(Locale.ENGLISH).getFirstDayOfWeek(); //it's SUNDAY