## 日付/時刻 の算術

以下の例は、DST(夏時間) への移行や、 異なった日を持つ月に関する 日付/時刻 の算術の罠をいくつか紹介します。

例1 DateTimeImmutable::add/sub は、経過時間を含む区間を足してしまう

DST (夏時間) への移行タイミングをまたいで 24時間を足してしまうと、 (ほとんどのタイムゾーンでは) 23/25時間を足しているように見える

    <?php
    $dt = new DateTimeImmutable("2015-11-01 00:00:00", new DateTimeZone("America/New_York"));
    echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
    $dt = $dt->add(new DateInterval("PT3H"));
    echo "End:   ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

上の例の出力は以下となります。

    Start: 2015-11-01 00:00:00 -04:00
    End:   2015-11-01 02:00:00 -05:00

例2 DateTimeImmutable::modify と strtotime を使い、個々のコンポーネントの値をインクリメント/デクリメントする

DST (夏時間) の移行のタイミングをまたいで24時間を足すと、日付/時刻 文字列に正確に24時間足されることになります (開始時刻と終了時刻が同じ移行ポイントにない場合) point).

    <?php
    $dt = new DateTimeImmutable("2015-11-01 00:00:00", new DateTimeZone("America/New_York"));
    echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
    $dt = $dt->modify("+24 hours");
    echo "End:   ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

上の例の出力は以下となります。

    Start: 2015-11-01 00:00:00 -04:00
    End:   2015-11-02 00:00:00 -05:00

例3 時刻を引き算すると、日付がオーバー/アンダーフローする可能性がある

1月31日 + 1ヶ月 のような計算をすると、(うるう年のときは) 3月2日、 または (通常の年は) 3月3日になります。

    <?php
    echo "Normal year:\n"; // February has 28 days
    $dt = new DateTimeImmutable("2015-01-31 00:00:00", new DateTimeZone("America/New_York"));
    echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
    $dt = $dt->modify("+1 month");
    echo "End:   ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

    echo "Leap year:\n"; // February has 29 days
    $dt = new DateTimeImmutable("2016-01-31 00:00:00", new DateTimeZone("America/New_York"));
    echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
    $dt = $dt->modify("+1 month");
    echo "End:   ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

上の例の出力は以下となります。

    Normal year:
    Start: 2015-01-31 00:00:00 -05:00
    End:   2015-03-03 00:00:00 -05:00
    Leap year:
    Start: 2016-01-31 00:00:00 -05:00
    End:   2016-03-02 00:00:00 -05:00

次の月の最後の日を得るために(つまり、オーバーフローを避けるため)、 `last day of` フォーマットも利用できます。

    <?php
    echo "Normal year:\n"; // February has 28 days
    $dt = new DateTimeImmutable("2015-01-31 00:00:00", new DateTimeZone("America/New_York"));
    echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
    $dt = $dt->modify("last day of next month");
    echo "End:   ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

    echo "Leap year:\n"; // February has 29 days
    $dt = new DateTimeImmutable("2016-01-31 00:00:00", new DateTimeZone("America/New_York"));
    echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
    $dt = $dt->modify("last day of next month");
    echo "End:   ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

上の例の出力は以下となります。

    Normal year:
    Start: 2015-01-31 00:00:00 -05:00
    End:   2015-02-28 00:00:00 -05:00
    Leap year:
    Start: 2016-01-31 00:00:00 -05:00
    End:   2016-02-29 00:00:00 -05:00

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open