简体   繁体   中英

ZonedDateTime toString compatability with ISO 8601

I am trying to ensure that calling toString() on my ZonedDateTime Object will comply with ISO-8601 format.

The documentation for the toString() method states:

...The output is compatible with ISO-8601 if the offset and ID are the same

Does this mean that there exists a situation where calling zdt.getOffset() will return something different than 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.

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:

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.

boolean iso8601Compatible = zdt.getZone() instanceof ZoneOffset;

To guarantee an ISO-8601 compatible output use toOffsetDateTime() :

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

or a formatter.

The example in the documentation is 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.

So the real question may in fact be the opposite: if ZonedDateTime.toString() includes the time zone information that ISO does not include, when is the 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.

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.

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.

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:mmZ instead of yyyy-MM-ddTHH:mm:ssZ . That can cause trouble if other system expects static format and has no adaptability.

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());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM