web-dev-qa-db-ja.com

ZonedDateTimeとISO 8601のString互換性

ZonedDateTimeオブジェクトのtoString()の呼び出しがISO-8601形式に準拠することを保証しようとしています。

toString()メソッドのドキュメントには次のように記載されています。

...オフセットとIDが同じ場合、出力はISO-8601と互換性があります

これは、zdt.getOffset()を呼び出すとzdt.getZone().getRules().getOffset(zdt.toInstant())とは異なるものが返される状況が存在することを意味しますか?

これは理にかなっていないようです。

誰かがオフセットとIDが同じではない例を提供できますか(つまり:toString()がISO-8601に準拠していない場合)、ドキュメントの説明をよりよく理解できます。

ありがとうございました。

15
theyuv

これは完全な仕様です:

_ * 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が使用される2番目のケースでは、toString()形式は最後の角括弧セクションを省略します。そのセクションを省略すると、結果はISO-8601互換になります。

_boolean iso8601Compatible = zdt.getZone() instanceof ZoneOffset;
_

ISO-8601互換の出力を保証するには、toOffsetDateTime()を使用します。

_String isoCompatible = zdt.toOffsetDateTime().toString();
_

またはフォーマッター。

29
JodaStephen

ドキュメント の例は_2007-12-03T10:15:30+01:00[Europe/Paris]_です。 ISO-8601には_[Europe/Paris]_部分が含まれていないため、これはISO準拠ではありません。これは_Java.time_開発者によって、妥当な範囲で標準に近づくことと、明確な方法でタイムゾーン情報を提供することの間の妥協点で追加されました。

したがって、実際の質問は実際には反対かもしれません:ZonedDateTime.toString()がISOに含まれないタイムゾーン情報を含む場合、is結果は完全にISOに準拠していますか? 「オフセットとIDが同じ場合」とはどういう意味ですか?ここで、ZoneOffsetZoneIDのサブクラスであり、ZonedDateTimeのゾーンIDとして使用できることを覚えておく必要があります。この場合、オフセットとIDは同じです。そうでなければ、そうではありません。特定の例では、ZonedDateTime.now(ZoneOffset.ofHours(+2)).toString()は_2017-04-26T15:04:59.828+02:00_を生成する場合があります。ゾーンは_+02:00_として指定されるため、これは完全にISO互換です。これはオフセットと同じです。また、ZonedDateTime.now(ZoneOffset.UTC).toString()は、形式_2017-04-26T13:04:59.828Z_で何かを提供します。 Zはオフセットとしてカウントされるため、これも互換性があります。

ほとんどの場合、あまり有用ではないと思います。ゾーンが単なるオフセットの場合、通常はOffsetDateTimeよりもZonedDateTimeを使用することを好むでしょう。もしそうであれば、ZonedDateTime.toString()がISO互換であるかどうかは気にしませんか否か。

5
Ole V.V.

私は、JodaStephenの最後のコメント'or formatter'が好きです。これは、データに関係なくフォーマッターを使用する方がより堅牢だからです。その理由は、OffsetDateTime toString()が2番目のユニット以下に値がないときに2番目のユニット部分をスキップするため、最終的にyyyy-MM-ddTHH:mmZyyyy-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
Steve Park

公式文書に従って:

各瞬間に正確に1つの有効なオフセットがあるため、瞬間のオフセットを取得するのは簡単です。対照的に、ローカル日時のオフセットを取得するのは簡単ではありません。次の3つのケースがあります。

  • 通常、1つの有効なオフセット。 1年の大半では、通常のケースが適用されます。ローカルの日付と時刻の有効なオフセットは1つだけです。
  • 有効なオフセットがゼロのギャップ。これは、通常、春の夏時間の変更が「冬」から「夏」に変わるために、時計が前方にジャンプするときです。ギャップには、有効なオフセットのないローカル日時値があります。
  • 2つの有効なオフセットを持つ重複。これは、通常、夏の「夏」から「冬」への夏時間の変更により、時計が遅れる場合です。オーバーラップには、2つの有効なオフセットを持つローカル日時値があります。

つまり、2番目と3番目のケースは、toString()がISO-8601に準拠しない状況です。

0
VivekRatanSinha