DateTimeFormatter工作日似乎偏离了一个 [英] DateTimeFormatter weekday seems off by one

查看:100
本文介绍了DateTimeFormatter工作日似乎偏离了一个的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将现有的应用程序从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.

在Joda-Time中,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).

java.time API中,模式e

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆