ZonedDateTime如何与ISO 8601兼容的toString方法

27

我正在尝试确保在我的ZonedDateTime对象上调用toString()方法符合ISO-8601格式。

toString()方法的文档说明如下:

 

...如果偏移量和ID相同,则输出与ISO-8601兼容的内容

这是否意味着存在一种情况,在该情况下调用zdt.getOffset()将返回与zdt.getZone().getRules().getOffset(zdt.toInstant())不同的内容?

这似乎是没有意义的。

请问有人可以提供一个示例,其中偏移量和ID不同(即toString()不符合ISO-8601),以便我更好地理解文档中的说明。

4个回答

49

这是完整规范:

 * 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规范指的是使用ZoneOffset而不是命名的ZoneId构建ZonedDateTime的情况,因此偏移量和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

可以看到,在第二种情况下使用 ZoneOffsettoString() 的格式省略了末尾的方括号部分。通过省略该部分,结果符合 ISO-8601 格式。

boolean iso8601Compatible = zdt.getZone() instanceof ZoneOffset;

为了保证ISO-8601兼容的输出,请使用toOffsetDateTime()

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

或者格式化程序。


12
“或者格式化程序”:DateTimeFormatter.ISO_OFFSET_DATE_TIME 这个想法浮现在脑海中。 - Ole V.V.
使用toOffsetDateTime()非常顺畅。 - tschumann

7
文档中的例子是2007-12-03T10:15:30+01:00[Europe/Paris]。这不符合ISO-8601标准,因为ISO标准不包含[Europe/Paris]部分。java.time开发人员通过折衷方式增加了该部分,使其在尽可能接近标准的同时以无歧义的方式提供时区信息。

那么实际上问题可能相反:如果ZonedDateTime.toString()包含ISO不包括的时区信息,那么结果何时才是完全符合ISO标准的?“如果偏移量和ID相同”是什么意思?我们必须记住,ZoneOffsetZoneID的子类,可以作为ZonedDateTime中的区域标识使用。在这种情况下,偏移量和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());

0

根据官方文档:

获取一个瞬间的偏移量很简单,因为每个瞬间只有一个有效的偏移量。相比之下,获取本地日期时间的偏移量并不容易。有三种情况:

  • 正常情况,只有一个有效的偏移量。在大多数情况下,正常情况适用,即本地日期时间只有一个有效的偏移量。
  • 间隙情况,没有有效的偏移量。这是指时钟向前跳跃,通常是由于春季日光节约时间从“冬季”到“夏季”的变化。在间隙中,存在没有有效偏移量的本地日期时间值。
  • 重叠情况,有两个有效的偏移量。这是指时钟向后设置,通常是由于秋季日光节约时间从“夏季”到“冬季”的变化。在重叠中,存在两个有效偏移量的本地日期时间值。

因此,第二种和第三种情况是toString()不符合ISO-8601标准的情况。


问题是关于ZonedDateTime而不是LocalDateTimeZonedDateTime可以唯一地标识一个瞬间,所以我看不出问题在哪里?另一方面,您是正确的,LocalDateTime和时区(ZoneId)在一起并不总是唯一地标识一个瞬间,正如您在情况2和3中解释的那样。 - Ole V.V.
@OleV.V. 你已经回答了自己的问题,我的朋友。在无法确定唯一的Instant的情况下,toString()将不符合ISO-8601标准。 - VivekRatanSinha
4
尽管这些信息很有趣,但不幸的是它们并不是问题的正确答案。在这种情况下,“Gaps”和“Overlaps”与ISO-8601毫无关系。 - JodaStephen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接