2017-04-26 80 views
7

我試圖確保在我的ZonedDateTime對象上調用toString()將符合ISO-8601格式。ZonedDateTime toString與ISO 8601的兼容性

toString()方法狀態的文檔:

...輸出爲ISO-8601兼容如果偏移和ID是 相同

這是否意味着存在 zdt.getOffset() 將返回一些不同於 zdt.getZone().getRules().getOffset(zdt.toInstant()) 的情況?

這似乎沒有道理。

有人可以提供一個例子,其中偏移量和ID不相同(即:其中toString()不符合ISO-8601),以便我可以更好地理解文檔中的描述。

謝謝。

回答

12

這是完整的規範:

* Outputs this date-time as a {@code String}, such as 
* {@code 2007-12-03T10:15:30+01:00[Europe/Paris]}. 
* <p> 
* The format consists of the {@code LocalDateTime} followed by the {@code ZoneOffset}. 
* If the {@code ZoneId} is not the same as the offset, then the ID is output. 
* The output is compatible with ISO-8601 if the offset and ID are the same. 

的Javadoc規範指的是ZonedDateTime被構造成具有ZoneOffset而非命名ZoneId的情況下,因此在偏移和ID是相同的:

System.out.println(ZonedDateTime.now(ZoneId.of("Europe/Paris"))); 
// 2017-04-26T15:13:12.006+02:00[Europe/Paris] 

System.out.println(ZonedDateTime.now(ZoneOffset.ofHours(2))); 
// 2017-04-26T15:13:12.016+02:00 

如可以看到的,在第二種情況下,當使用一個ZoneOffset,所述toString()格式省略末尾的括號部分。通過省略該部分,結果是ISO-8601兼容。

boolean iso8601Compatible = zdt.getZone() instanceof ZoneOffset; 

爲了保證一個ISO-8601兼容的輸出使用toOffsetDateTime()

String isoCompatible = zdt.toOffsetDateTime().toString(); 

或格式化。

+4

「或格式化程序」:[DateTimeFormatter.ISO_OFFSET_DATE_TIME](http://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE_TIME)。 –

0

按照官方文檔:

獲取的瞬間偏移很簡單,因爲有只有一個每個瞬間有效抵消。相比之下,獲取本地日期時間的偏移量並不簡單。有三種情況:

  • 正常,有一個有效的偏移量。對於一年中絕大多數時間,正常情況下適用,當地日期時間有一個有效偏移量。
  • 間隙,零有效偏移量。這通常是由於春季夏令時從「冬季」變爲「夏季」時,時鐘向前跳躍。在缺口中存在沒有有效偏移量的本地日期 - 時間值。
  • 重疊,有兩個有效的偏移量。這通常是由於秋季夏令時從「夏季」變爲「冬季」,因此時鐘退回。在重疊處有本地日期時間值和兩個有效偏移量。

所以第二和第三種情況是toString()不符合ISO-8601的情況。

+0

問題是關於'ZonedDateTime',而不是'LocalDateTime'。 'ZonedDateTime'唯一標識一個瞬間,所以我沒有看到問題?另一方面,正如你在案例2和3中解釋的,LocalDateTime和時區(ZoneId)並不總是唯一標識一個時刻。 –

+0

@ OleV.V。你已經回答了你自己的問題,我的朋友。對於無法識別唯一Instant的情況,toString()將不符合ISO-8601標準。 – VivekRatanSinha

+3

儘管這些信息很有趣,但不幸的是這個問題的答案是錯誤的。在這種情況下,間隙和重疊與ISO-8601無關。請記住 – JodaStephen

1

the documentation中的示例爲2007-12-03T10:15:30+01:00[Europe/Paris]。由於ISO-8601不包括[Europe/Paris]部件,所以這不符合ISO標準。這是java.time開發人員之間的妥協,在儘可能接近標準的合理之間進行妥協,並仍以明確的方式提供時區信息。

所以真正的問題可能實際上是相反的:如果ZonedDateTime.toString()包括時區信息ISO不包括,當結果完全符合ISO標準? 「如果偏移和ID是相同的」是什麼意思?在這裏,我們必須記住ZoneOffsetZoneID的一個子類,可用作ZonedDateTime中的區域ID。在這種情況下,偏移量和ID是相同的。否則,他們不是。舉一個具體的例子,ZonedDateTime.now(ZoneOffset.ofHours(+2)).toString()可能產生2017-04-26T15:04:59.828+02:00。這完全是ISO兼容的,因爲該區域僅爲+02:00,與偏移量相同。 ZonedDateTime.now(ZoneOffset.UTC).toString()也給出了2017-04-26T13:04:59.828Z的格式。由於Z計算爲抵消,所以這也是兼容的。

我認爲在大多數情況下它不會非常有用。如果你的防區只是一個偏移量,你通常更喜歡使用OffsetDateTime而不是ZonedDateTime,如果是這樣,當然你不在乎ZonedDateTime.toString()是否兼容ISO。

0

我喜歡以上JodaStephen的最新評論'或格式化程序',因爲使用格式化程序無論數據如何都更健壯。 原因是OffsetDateTime的toString(),則跳過第二單元部分當存在第二單元和下方上沒有價值,所以它最終給出YYYY-MM-DDTHH:MMZ代替YYYY-MM-DDTHH:MM:SSZ 。如果其他系統需要靜態格式並且沒有適應性,則會造成麻煩。

下面是我用來模擬兩個沒有時間部分的情況的代碼。

/** 
* This function is design to convert Date object from other system to ISO dateTime String 
* which will be sent to another system. and using formatter to lock up the format 
* @param date java.util.Date 
* @return 'yyyy-MM-ddTHH:mm:ssZ' format ISO dateTime string 
*/ 
public String formatIsoUtcDateTime(Date date) { 
    if(null == date) { 
     return null; 
    } 
    return ZonedDateTime.ofInstant(date.toInstant(), ZoneId.of("UTC")) 
         .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")); 
} 

// no time portion with using formatter 
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); 
Date date = sdf.parse("20171010"); 
System.out.println(formatIsoUtcDateTime(date)); 

// no time portion with using OffsetDateTime toString 
System.out.println(ZonedDateTime.ofInstant(date.toInstant(), ZoneId.of("UTC")).toOffsetDateTime().toString()); 

// just current date and probably has at least millisecond, using formatter 
System.out.println(formatIsoUtcDateTime(new Date())); 

// just current date and probably has at least millisecond, using OffsetDateTime toString 
// this will print yyyy-MM-ddTHH:mm:ss.SSSZ format 
System.out.println(ZonedDateTime.ofInstant(new Date().toInstant(), ZoneId.of("UTC")).toOffsetDateTime().toString());