22

我试图确保调用toString()我的ZonedDateTime对象将符合 ISO-8601 格式。

toString()方法的文档指出:

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

这是否意味着存在调用 zdt.getOffset() 将返回与 不同的东西 的情况zdt.getZone().getRules().getOffset(zdt.toInstant())

这似乎没有意义。

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

4

4 回答 4

40

这是完整的规范:

 * 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是使用 aZoneOffset而不是 named构造的情况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

可以看出,在第二种情况下,使用 aZoneOffset时,toString()格式省略了末尾的方括号部分。通过省略该部分,结果与 ISO-8601 兼容。

boolean iso8601Compatible = zdt.getZone() instanceof ZoneOffset;

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

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

或格式化程序。

于 2017-04-26T13:15:48.160 回答
7

文档中的示例是2007-12-03T10:15:30+01:00[Europe/Paris]. 这恰好不符合 ISO 标准,因为 ISO-8601 不包括该[Europe/Paris]部分。这是java.time开发人员在尽可能接近标准和仍然以明确的方式提供时区信息之间进行折衷而添加的。

所以真正的问题实际上可能是相反的:如果ZonedDateTime.toString()包括 ISO 不包括的时区信息,那么结果何时完全符合 ISO 标准?“如果偏移量和 ID 相同”是什么意思?在这里我们必须记住,它ZoneOffset是 的子类,ZoneID并且可以用作 中的区域 ID ZonedDateTime。在这种情况下,偏移量和 ID 相同。否则他们不是。对于一个具体的例子,ZonedDateTime.now(ZoneOffset.ofHours(+2)).toString()可能会产生2017-04-26T15:04:59.828+02:00. 这是完全 ISO 兼容的,因为该区域被指定为 just +02:00,与偏移量相同。还ZonedDateTime.now(ZoneOffset.UTC).toString()给出了格式的东西2017-04-26T13:04:59.828Z。由于Z算作偏移,这也是兼容的。

我认为在大多数情况下它不会很有用。如果您的区域只是一个偏移量,您通常更喜欢使用OffsetDateTimeover ZonedDateTime,如果是这样,您当然不在乎是否ZonedDateTime.toString()与 ISO 兼容。

于 2017-04-26T13:16:35.483 回答
0

根据官方文档:

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

  • 正常,具有一个有效偏移量。对于一年中的绝大多数时间,正常情况适用,本地日期时间有一个有效的偏移量。
  • 间隙,有效偏移量为零。这通常是由于春季夏令时从“冬季”变为“夏季”,时钟向前跳动的时候。在间隙中存在没有有效偏移的本地日期时间值。
  • 重叠,具有两个有效的偏移量。这是由于秋季夏令时从“夏季”变为“冬季”,时钟通常会被调回。在重叠中,有两个有效偏移量的本地日期时间值。

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

于 2017-04-26T12:51:41.437 回答
0

我喜欢 JodaStephen 的最后一条评论'or a formatter',因为无论数据如何,使用 formatter 都更加健壮。原因是 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());
于 2017-09-12T22:33:25.093 回答