DateTimeFormatter 工作日似乎减一
DateTimeFormatter weekday seems off by one
我正在将现有应用程序从 Joda-Time 移植到 Java 8 java.time
.
我 运行 遇到一个问题,在我的单元测试中解析包含 'day of week' 值的 date/time 字符串触发了异常。
解析时:
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
.
现在对于 java.time.DateTimeFormatter
支持的模式,我在 the Oracle documentation 和 JSR-310 中都找到了这个:
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
这个 2
和 'Tuesday' 的明确例子让我相信星期三在 java.time 中也应该是 3
而不是 4
。
这里有什么问题?
我误解了吗?
这是 Java 8 中的错误吗?
在 Locale.ENGLISH
中,星期三是一周的第 4 天,因为一周从星期日开始。
您可以使用
检查一周的第一天
WeekFields.of(Locale.ENGLISH).getFirstDayOfWeek(); //it's SUNDAY
Joda-Time 和 java.time
解释模式的方式不同 e
。
在 Joda-Time 中,e
模式 designates the numeric value of day-of-week:
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()
return 3
。 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
has a different meaning:
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()
returns 3
.
那是因为 e
与英语区域设置等同于使用 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 默认语言环境,如果设置了 none)。例如,在法语语言环境中,它的行为就像 ISO,而在某些阿拉伯语语言环境中,一周的第一天是星期六:
WeekFields.of(Locale.FRENCH).getFirstDayOfWeek(); // MONDAY
WeekFields.of(new Locale("ar", "AE")).getFirstDayOfWeek(); // SATURDAY
根据 javadoc,return 星期几的数值的唯一模式似乎是本地化模式。因此,要解析输入 2016-12-21 20:50:25 Wednesday December +0000 3
,您可以使用 java.time.format.DateTimeFormatterBuilder
并将 date/time 模式与 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);
另请注意,您不需要引用 -
、:
和 space 字符,因此模式变得更加清晰易读 (IMO)。
我还设置了英文语言环境,因为如果你不设置,它将使用 JVM 默认语言环境,并且不能保证始终是英语。它也可以在不通知的情况下更改,即使在运行时也是如此,因此最好指定一个,特别是如果您已经知道输入的语言是什么。
更新:可能 ccccc
模式应该有效,因为它等同于 appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW_STANDALONE)
并且在我的测试中(JDK 1.8.0_144) 它 returns (并且还解析) 3
:
DateTimeFormatter parser = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss EEEE MMMM ZZ ccccc", Locale.ENGLISH);
ZonedDateTime date = ZonedDateTime.parse(input, parser);
我正在将现有应用程序从 Joda-Time 移植到 Java 8 java.time
.
我 运行 遇到一个问题,在我的单元测试中解析包含 'day of week' 值的 date/time 字符串触发了异常。
解析时:
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
.
现在对于 java.time.DateTimeFormatter
支持的模式,我在 the Oracle documentation 和 JSR-310 中都找到了这个:
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
这个 2
和 'Tuesday' 的明确例子让我相信星期三在 java.time 中也应该是 3
而不是 4
。
这里有什么问题? 我误解了吗? 这是 Java 8 中的错误吗?
在 Locale.ENGLISH
中,星期三是一周的第 4 天,因为一周从星期日开始。
您可以使用
WeekFields.of(Locale.ENGLISH).getFirstDayOfWeek(); //it's SUNDAY
Joda-Time 和 java.time
解释模式的方式不同 e
。
在 Joda-Time 中,e
模式 designates the numeric value of day-of-week:
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()
return 3
。 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
has a different meaning:
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()
returns 3
.
那是因为 e
与英语区域设置等同于使用 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 默认语言环境,如果设置了 none)。例如,在法语语言环境中,它的行为就像 ISO,而在某些阿拉伯语语言环境中,一周的第一天是星期六:
WeekFields.of(Locale.FRENCH).getFirstDayOfWeek(); // MONDAY
WeekFields.of(new Locale("ar", "AE")).getFirstDayOfWeek(); // SATURDAY
根据 javadoc,return 星期几的数值的唯一模式似乎是本地化模式。因此,要解析输入 2016-12-21 20:50:25 Wednesday December +0000 3
,您可以使用 java.time.format.DateTimeFormatterBuilder
并将 date/time 模式与 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);
另请注意,您不需要引用 -
、:
和 space 字符,因此模式变得更加清晰易读 (IMO)。
我还设置了英文语言环境,因为如果你不设置,它将使用 JVM 默认语言环境,并且不能保证始终是英语。它也可以在不通知的情况下更改,即使在运行时也是如此,因此最好指定一个,特别是如果您已经知道输入的语言是什么。
更新:可能 ccccc
模式应该有效,因为它等同于 appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW_STANDALONE)
并且在我的测试中(JDK 1.8.0_144) 它 returns (并且还解析) 3
:
DateTimeFormatter parser = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss EEEE MMMM ZZ ccccc", Locale.ENGLISH);
ZonedDateTime date = ZonedDateTime.parse(input, parser);