2017-04-26 79 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());