DateTimeFormatter工作日似乎偏离了一个 [英] DateTimeFormatter weekday seems off by one
问题描述
我正在将现有的应用程序从Joda-Time移植到Java 8 java.time
.
I'm porting an existing application from Joda-Time to Java 8 java.time
.
我遇到了一个问题,其中解析包含星期几"值的日期/时间字符串会触发我的单元测试中的异常.
I ran into a problem where parsing a date/time string that contains a 'day of week' value triggered an exception in my unit tests.
解析时:
2016-12-21 20:50:25 12月星期三+0000 3
2016-12-21 20:50:25 Wednesday December +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
.
So effectively the root cause of the problem is that the e
flag in Joda-Time considers Monday to be 1
yet the Java 8 java.time
considers Monday to be 0
.
Now for the patterns that java.time.DateTimeFormatter
supports I find in both the Oracle documentation and in JSR-310 this:
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
这个明确的示例2
和'星期二'使我相信java.time中的周三也应该是3
而不是4
.
This explicit example of 2
and 'Tuesday' leads me to believe that Wednesday should also in java.time be 3
instead of 4
.
这是怎么了? 我会误会吗? 这是Java 8中的错误吗?
What is wrong here? Do I misunderstand? Is this a bug in Java 8?
推荐答案
Joda-Time和java.time
解释模式e
的方式有所不同.
There's a difference on how Joda-Time and java.time
interprets the pattern e
.
In Joda-Time, the e
pattern designates the numeric value of day-of-week:
Symbol Meaning Presentation Examples
------ ----------- ------------ -------
e day of week number 2
因此,使用e
等同于从日期对象获取星期几:
So, using e
is equivalent to getting the day of the week from a date object:
// 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
(一周中的第三天 https://www.timeanddate.com/calendar/days/"rel =" noreferrer> ISO的定义).
Note that both the formatter and getDayOfWeek()
return 3
. The getDayOfWeek()
method returns a value defined in DateTimeConstants
class, and Wednesday's value is 3
(the third day of the week according to ISO's definition).
In java.time
API, the pattern e
has a different meaning:
Pattern Count Equivalent builder methods
------- ----- --------------------------
e 1 append special localized WeekFields element for numeric day-of-week
它使用本地化的 WeekFields
元素,并且可以根据区域设置而有所不同.与getDayOfWeek()
方法相比,该行为可能有所不同:
It uses the localized WeekFields
element, and this can vary according to the locale. The behaviour might be different when compared to the getDayOfWeek()
method:
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
.
Note that the formatter uses the localized day of week for English locale, and the value is 4
, while calling getDayOfWeek().getValue()
returns 3
.
这是因为具有英语语言环境的e
等同于使用java.time.temporal.WeekFields
:
That's because e
with English locale is equivalent to using a java.time.temporal.WeekFields
:
// using localized fields
WeekFields wf = WeekFields.of(Locale.ENGLISH);
System.out.println(z.get(wf.dayOfWeek())); // 4
getDayOfWeek()
等同于使用ISO的定义:
While getDayOfWeek()
is equivalent to using ISO's definition:
// same as getDayOfWeek()
System.out.println(z.get(WeekFields.ISO.dayOfWeek())); // 3
这是因为ISO的定义使用星期一作为一周的第一天,而使用英语语言环境的WeekFields
使用星期日:
That's because ISO's definition uses Monday as the first day of the week, while WeekFields
with English locale uses Sunday:
// comparing the first day of week
System.out.println(WeekFields.ISO.getFirstDayOfWeek()); // MONDAY
System.out.println(wf.getFirstDayOfWeek()); // SUNDAY
因此,根据格式化程序中设置的语言环境(或JVM默认语言环境,如果未设置),e
模式的行为可能与getDayOfWeek()
不同.例如,在法国语言环境中,它的行为类似于ISO,而在某些阿拉伯语环境中,一周的第一天是星期六:
So the e
pattern might behave differently or not to getDayOfWeek()
, according to the locale set in the formatter (or the JVM default locale, if none is set). In French locale, for example, it behaves just like ISO, while in some arabic locales, the first day of the week is Saturday:
WeekFields.of(Locale.FRENCH).getFirstDayOfWeek(); // MONDAY
WeekFields.of(new Locale("ar", "AE")).getFirstDayOfWeek(); // SATURDAY
According to javadoc, the only patterns that return a numeric value for the day of week seem to be the localized ones. So, to parse the input 2016-12-21 20:50:25 Wednesday December +0000 3
, you can use a java.time.format.DateTimeFormatterBuilder
and join the date/time pattern with a java.time.temporal.ChronoField
to indicate the numeric value of the day of week (the ISO non-locale sensitive field):
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).
Also note that you don't need to quote the -
, :
and space characters, so the pattern becomes more clear and readable (IMO).
我还设置了英语语言环境,因为如果未设置,它将使用JVM默认语言环境,并且不能保证始终为英语.而且即使在运行时也可以在不通知的情况下进行更改,因此最好指定一个,特别是如果您已经知道输入的语言.
I also set the English locale, because if you don't set, it'll use the JVM default locale, and it's not guaranteed to always be English. And it can also be changed without notice, even at runtime, so it's better to specify one, specially if you already know in what language the input is.
更新:也许ccccc
模式应该可以工作,因为它等效于appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW_STANDALONE)
,并且在我的测试(JDK 1.8.0_144)中,它返回(并解析)3
:
Update: probably the ccccc
pattern should work, as it's equivalent to appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW_STANDALONE)
and in my tests (JDK 1.8.0_144) it returns (and also parses) 3
:
DateTimeFormatter parser = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss EEEE MMMM ZZ ccccc", Locale.ENGLISH);
ZonedDateTime date = ZonedDateTime.parse(input, parser);
这篇关于DateTimeFormatter工作日似乎偏离了一个的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!