java ZonedDateTime toString 与 ISO 8601 的兼容性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/43634226/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ZonedDateTime toString compatability with ISO 8601
提问by theyuv
I am trying to ensure that calling toString()
on my ZonedDateTime
Object will comply with ISO-8601 format.
我试图确保toString()
对我的ZonedDateTime
对象的调用符合 ISO-8601 格式。
The documentation for the toString()
method states:
该toString()
方法的文档指出:
...The output is compatible with ISO-8601 if the offset and ID are the same
...如果偏移量和 ID 相同,则输出与 ISO-8601 兼容
Does this mean that there exists a situation where calling
zdt.getOffset()
will return something different than
zdt.getZone().getRules().getOffset(zdt.toInstant())
?
这是否意味着存在调用
zdt.getOffset()
将返回与
zdt.getZone().getRules().getOffset(zdt.toInstant())
?
This doesn't seem to make sense.
这似乎没有意义。
Can someone provide an example in which the offset and ID are not the same (ie: where toString()
does not comply with ISO-8601) so that I can better understand the description in the documentation.
有人可以提供一个偏移量和 ID 不相同的示例(即:哪里toString()
不符合 ISO-8601),以便我可以更好地理解文档中的描述。
回答by JodaStephen
This is the complete specification:
这是完整的规范:
* 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.
The Javadoc specification refers to the case where the ZonedDateTime
is constructed with a ZoneOffset
rather than a named ZoneId
, thus where 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
As can be seen, in the second case, where a ZoneOffset
is used, the toString()
format omits the square bracket section at the end. By omitting that section, the result is ISO-8601 compatible.
可以看出,在ZoneOffset
使用a 的第二种情况下,toString()
格式省略了末尾的方括号部分。通过省略该部分,结果与 ISO-8601 兼容。
boolean iso8601Compatible = zdt.getZone() instanceof ZoneOffset;
To guarantee an ISO-8601 compatible output use toOffsetDateTime()
:
为保证 ISO-8601 兼容输出使用toOffsetDateTime()
:
String isoCompatible = zdt.toOffsetDateTime().toString();
or a formatter.
或格式化程序。
回答by Ole V.V.
The example in the documentationis 2007-12-03T10:15:30+01:00[Europe/Paris]
. This happens not to be ISO compliant since ISO-8601 does not include the [Europe/Paris]
part. This was added by the java.time
developers in a compromise between getting as close to the standard as reasonable and still provding the time zone information in an unambiguous way.
文档中的示例是2007-12-03T10:15:30+01:00[Europe/Paris]
. 这恰好不符合 ISO 标准,因为 ISO-8601 不包括该[Europe/Paris]
部件。这是java.time
开发人员在尽可能接近标准和仍然以明确的方式提供时区信息之间的折衷中添加的。
So the real question may in fact be the opposite: if ZonedDateTime.toString()
includes the time zone information that ISO does not include, when isthe result fully ISO compliant? What does “if the offset and ID are the same” mean? Here we have to remember that ZoneOffset
is a subclass of ZoneID
and may be used as a zone ID in ZonedDateTime
. In this case the offset and the ID are the same. Otherwise they are not. For a specific example, ZonedDateTime.now(ZoneOffset.ofHours(+2)).toString()
may produce 2017-04-26T15:04:59.828+02:00
. This is fully ISO compatible because the zone is given as just +02:00
, which is the same as the offset. Also ZonedDateTime.now(ZoneOffset.UTC).toString()
gives something in the format 2017-04-26T13:04:59.828Z
. Since Z
counts as an offset, this is compatible too.
所以真正的问题可能实际上是相反的:如果ZonedDateTime.toString()
包括时区信息ISO不包括,当是结果完全符合ISO标准?“如果偏移量和 ID 相同”是什么意思?这里我们要记住,它ZoneOffset
是 的子类,ZoneID
可以用作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
算作偏移量,因此这也是兼容的。
I think that in most cases it won't be very useful. If your zone is just an offset, you would usually prefer to use OffsetDateTime
over ZonedDateTime
, and if so, of course you don't care whether ZonedDateTime.toString()
is ISO compatible or not.
我认为在大多数情况下它不会很有用。如果您的区域只是一个偏移量,您通常更愿意使用OffsetDateTime
over ZonedDateTime
,如果是这样,您当然不在乎是否ZonedDateTime.toString()
与 ISO 兼容。
回答by Steve Park
I like above JodaStephen's last comment 'or a formatter', since using formatter is more robust regardless of data. The reason is OffsetDateTime toString() skip second unit part when there is no value on second unit and below, so it ends up gives yyyy-MM-ddTHH:mmZinstead of yyyy-MM-ddTHH:mm:ssZ. That can cause trouble if other system expects static format and has no adaptability.
我喜欢上面 JodaStephen 的最后一条评论'or a formatter',因为无论数据如何,使用格式化程序都更加健壮。原因是 OffsetDateTime toString() 在第二个单位及以下没有值时跳过第二个单位部分,因此最终给出yyyy-MM-ddTHH:mmZ而不是yyyy-MM-ddTHH:mm:ssZ。如果其他系统需要静态格式并且没有适应性,这可能会导致麻烦。
Below is the code I used for simulating both cases where has no time portion and has it.
下面是我用来模拟没有时间部分和有时间部分的两种情况的代码。
/**
* 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());
回答by VivekRatanSinha
As per the official documentation:
根据官方文档:
Obtaining the offset for an instant is simple, as there is exactly one valid offset for each instant. By contrast, obtaining the offset for a local date-time is not straightforward. There are three cases:
获取瞬间的偏移量很简单,因为每个瞬间都只有一个有效的偏移量。相比之下,获取本地日期时间的偏移量并不简单。有以下三种情况:
- Normal, with one valid offset. For the vast majority of the year, the normal case applies, where there is a single valid offset for the local date-time.
- Gap, with zero valid offsets. This is when clocks jump forward typically due to the spring daylight savings change from "winter" to "summer". In a gap there are local date-time values with no valid offset.
- Overlap, with two valid offsets. This is when clocks are set back typically due to the autumn daylight savings change from "summer" to "winter". In an overlap there are local date-time values with two valid offsets.
- 正常,有一个有效的偏移量。对于一年中的大部分时间,正常情况适用,其中本地日期时间有一个有效的偏移量。
- 间隙,有效偏移为零。这是通常由于春季夏令时从“冬天”变为“夏天”而使时钟向前跳的时候。在间隙中有没有有效偏移量的本地日期时间值。
- 重叠,有两个有效的偏移量。这是通常由于秋季夏令时从“夏季”变为“冬季”而导致时钟倒退的时候。在重叠中,存在具有两个有效偏移量的本地日期时间值。
So the 2nd and 3rd case are the situations where toString() will not be ISO-8601 compliant.
所以第二种和第三种情况是 toString() 不符合 ISO-8601 的情况。