这失败了。
OffsetDateTime.now()
.format(
DateTimeFormatter.ofLocalizedDateTime( FormatStyle.LONG )
) // throws DateTimeException.
但ZonedDateTime
中同一偏移的同一时刻起作用。
为什么?
让java.time
通过OffsetDateTime
自动本地化DateTimeFormatter.ofLocalizedDateTime
的字符串表示形式时,如果格式化程序带有format
SHORT
,则调用FormatStyle
会有效}或MEDIUM
。但是当格式化程序带有LONG
或FULL
时,会抛出DateTimeException
。然而ZonedDateTime
成功地使用相同的时刻offset。为什么呢?
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.LONG ) ;
OffsetDateTime odt = OffsetDateTime.now( ZoneId.systemDefault() ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( odt.getOffset() ) ; // Generate a `ZonedDateTime` with same moment and same offset as the `OffsetDateTime`.
// Succeeds.
String outputZdt = zdt.format( f ) ;
System.out.println( "outputZdt: " + outputZdt ) ;
// Fails. Throws exception.
if ( false ) {
String outputOdt = odt.format( f ) ; // Throws exception.
System.out.println( "outputOdt: " + outputOdt ) ;
}
请参阅此code run live at IdeOne.com。
跑步时......
好的。
outputZdt:2017年9月16日上午8:42:14 Z
坏。
Exception in thread "main" java.time.DateTimeException: Unable to extract value: class java.time.OffsetDateTime
at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:282)
at java.time.format.DateTimeFormatterBuilder$ZoneTextPrinterParser.format(DateTimeFormatterBuilder.java:3682)
at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2179)
at java.time.format.DateTimeFormatterBuilder$LocalizedPrinterParser.format(DateTimeFormatterBuilder.java:4347)
at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2179)
at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1746)
at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1720)
at java.time.OffsetDateTime.format(OffsetDateTime.java:1674)
at Ideone.main(Main.java:28)
我编写了该代码的核心来解决抛出的异常odt.atZoneSameInstant( odt.getOffset() )
。然后我意识到,为什么java.time
内部没有做同样的事情?为什么OffsetDateTime
无法格式化具有相同时刻和相同偏移的ZonedDateTime
成功的位置?我为什么需要从OffsetDateTime
转换为ZonedDateTime
?
{OffsetDateTime
格式化的这种行为是不是一个错误或一个功能?
我会提交错误报告,但我想确保我误解了一些内容。
答案 0 :(得分:4)
看起来Javadoc错误报告了here。在提供的示例中,他们使用LocalDateTime
,但行为是相同的。
使用FormatStyle.LONG
和FormatStyle.FULL
seems要求ZoneId
OffsetDateTime
没有<{p}}
请查看
java.time
javadoc改进以突出显示常见内容 关于格式化需要时区的元素的误解 除了时间。使用特定于语言环境的格式时,如果使用语言环境,则可能会有效 格式化不需要时区或如果语言环境失败 格式化需要时区,并且不提供时区。
这就是they clarified the javadoc提到
的原因* The {@code FULL} and {@code LONG} styles typically require a time-zone.
* When formatting using these styles, a {@code ZoneId} must be available,
* either by using {@code ZonedDateTime} or {@link DateTimeFormatter#withZone}.
您可以使用DateTimeFormatter
的{{1}}创建OffsetDateTime
。
ZoneOffset
在这种情况下,DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)
.withZone(odt.getOffset());
将在格式化之前转换为OffsetDateTime
。
答案 1 :(得分:1)
调试代码,我发现格式化程序最终在this line中(grepcode的行与我的JDK安装的编号不完全相同,但代码是):
ZoneId zone = context.getValue(TemporalQueries.zoneId());
它尝试使用内置查询TemporalQueries.zoneId()
提取区域。根据{{3}},如果时态对象是null
,则此查询返回OffsetDateTime
:
因此ZonedDateTime将返回getZone()的结果,但OffsetDateTime将返回null。
您可以通过致电odt.query(TemporalQueries.zoneId())
来确认这一点 - 它确实会返回null
。
稍后,此查询的结果为javadoc:
R result = temporal.query(query);
if (result == null && optional == 0) {
throw new DateTimeException("Unable to extract value: " + temporal.getClass());
}
由于result
为null
,它会抛出异常。
实际上,尝试从z
获取区域名称(模式OffsetDateTime
)会引发异常:
// java.time.DateTimeException: Unable to extract value: class java.time.OffsetDateTime
DateTimeFormatter.ofPattern("z").format(OffsetDateTime.now());
因为这种模式最终出现在上述问题中。
使用getLocalizedDateTimePattern
:
// did this for all locales
DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.LONG, FormatStyle.LONG,
IsoChronology.INSTANCE, locale);
我没有全部检查,但大多数都使用小写z
模式,这意味着它对大多数(如果不是全部)语言环境都会失败。
没有直接关联,但是当您使用atZoneSameInstant
作为参数调用ZoneOffset
时,您只需拨打odt.toZonedDateTime()
即可。