我正在将现有的应用程序从Joda-Time移植到Java 8 java.time
。
我遇到了一个问题,解析包含“周一天”值的日期/时间字符串在我的单元测试中触发了异常。
分析时:
2016年12月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
因此,问题的根本原因在于Joda-Time中的e
标志认为周一为1
,而Java8 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 be 3
而不是4
。
这里怎么了?我误解了吗?这是Java 8中的一个bug吗?
发布于 2017-09-21 04:17:15
在Joda-Time和java.time
如何解释模式e
上有一个不同之处。
在Joda-Time中,e
模式指定每周一天的数值。
Symbol Meaning Presentation Examples
------ ----------- ------------ -------
e day of week number 2
因此,使用e
相当于从date对象获取一周中的一天:
// 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
。方法返回在班级和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
等同于使用java.time.temporal.WeekFields
// using localized fields
WeekFields wf = WeekFields.of(Locale.ENGLISH);
System.out.println(z.get(wf.dayOfWeek())); // 4
getDayOfWeek()
等同于使用国际标准化组织的定义:
// same as getDayOfWeek()
System.out.println(z.get(WeekFields.ISO.dayOfWeek())); // 3
这是因为国际标准化组织的定义使用星期一作为一周的第一天,而带有英语语言环境的WeekFields
使用的是星期日:
// comparing the first day of week
System.out.println(WeekFields.ISO.getFirstDayOfWeek()); // MONDAY
System.out.println(wf.getFirstDayOfWeek()); // SUNDAY
因此,根据格式化程序中的区域设置(如果没有设置e
默认区域设置),getDayOfWeek()
模式的行为可能与getDayOfWeek()
不同。例如,在法语地区,它的行为就像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
并将日期/时间模式与java.time.temporal.ChronoField
连接起来,以指示一周中的数值( indicate敏感字段):
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);
发布于 2017-09-21 02:55:58
在Locale.ENGLISH
,星期三是一周中的第四天,因为周从星期天开始。你可以检查一周的第一天
WeekFields.of(Locale.ENGLISH).getFirstDayOfWeek(); //it's SUNDAY
https://stackoverflow.com/questions/46341152
复制相似问题