2017-09-16 156 views
2

tl; dr

失敗。OffsetDateTime失敗,使用本地化格式化程序使用FormatStyle的LONG或FULL

OffsetDateTime.now() 
       .format( 
        DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG) 
      ) // throws DateTimeException. 

但是在ZonedDateTime工作中具有相同偏移量的同一時刻。

爲什麼?

詳細

當讓java.time通過DateTimeFormatter.ofLocalizedDateTime自動本地化OffsetDateTime的字符串表示,呼籲format作品,如果格式化攜帶SHORTMEDIUM一個FormatStyle。但是,如果格式化程序包含LONGFULL,則會引發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分十四秒ž

壞。

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具有相同的時刻和相同的偏移成功?爲什麼我需要從OffsetDateTimeZonedDateTime這個轉換?

OffsetDateTime格式化失敗的行爲是一個錯誤還是一個功能?

我會提交一個錯誤報告,但我想確保我誤解了一些東西。

回答

1

調試代碼,我發現,格式化的this line結束(grepcode的路線是不完全一樣的號碼我的JDK安裝的,但代碼):

ZoneId zone = context.getValue(TemporalQueries.zoneId()); 

它試圖提取該區域使用內置查詢TemporalQueries.zoneId()。根據javadoc,該查詢返回null如果時間對象是OffsetDateTime

這樣一個ZonedDateTime將返回getZone()的結果,而是一個OffsetDateTime將返回null。

您可以致電odt.query(TemporalQueries.zoneId())確認 - 確實返回null

後來,這個查詢的結果是checked by a DateTimePrintContext

R result = temporal.query(query); 
if (result == null && optional == 0) { 
    throw new DateTimeException("Unable to extract value: " + temporal.getClass()); 
} 

由於resultnull,它會拋出異常。由於這種模式在上述問題的行結束

// java.time.DateTimeException: Unable to extract value: class java.time.OffsetDateTime 
DateTimeFormatter.ofPattern("z").format(OffsetDateTime.now()); 


事實上,從OffsetDateTime試圖讓區域名稱(模式z)將拋出一個異常。

,並檢查所有語言環境的日期風格,採用getLocalizedDateTimePattern

// did this for all locales 
DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.LONG, FormatStyle.LONG, 
    IsoChronology.INSTANCE, locale); 

我沒有檢查所有,但大多有小寫z模式,這意味着它會爲最失敗的(如果不是全部)語言環境。


沒有直接關係,但你與ZoneOffset作爲參數調用atZoneSameInstant,你可以簡單地調用odt.toZonedDateTime()代替。

3

看起來像是報告的Javadoc錯誤here。在提供的例子中,他們使用LocalDateTime,但行爲是相同的。

採用FormatStyle.LONGFormatStyle.FULLseems需要一個ZoneIdOffsetDateTime沒有

請仔細閱讀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}. 

你能創造的DateTimeFormatterOffsetDateTimeZoneOffset

DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG) 
            .withZone(odt.getOffset()); 

在這種情況下的格式發生之前OffsetDateTime將被轉換爲一個ZonedDateTime