DateTime — 日時の生成・操作(OOP/手続き型の両対応)

PHP
  • カテゴリ: PHP
  • 対応バージョン: PHP 8.2(※PHP標準クラス。全バージョンで利用可)
  • 名前空間 / FQCN / コマンド: DateTime / DateTimeImmutable / DateTimeZone
  • 関連: date / strtotime / DateInterval / DateTimeImmutable / DateTimeZone
  • 変更履歴: PHP 8 以降で型・例外まわりが安定。DateTimeImmutable 推奨ケース増加

要点(TL;DR)

  • 日時の生成・変換・加減算・差分を安全に扱うための標準クラス(手続き型関数も提供)
  • 最低限:(new DateTime('now', new DateTimeZone('Asia/Tokyo')))->format('Y-m-d H:i:s')
  • 罠:可変(DateTime)と不変(DateTimeImmutable)の違いタイムゾーン既定値createFromFormatパース失敗(例外/false)

概要

DateTime は日時の表現と操作を担うPHP標準クラスです。format() による文字列化、modify() による相対演算、diff() による差分取得などを提供します。破壊的変更を避けたい場合は DateTimeImmutable を使います。フレームワークに依存せず、Laravelでもそのまま利用できます。

構文 / シグネチャ

// OOP(推奨)
$dt  = new DateTime(string $datetime = "now", ?DateTimeZone $tz = null);
$dt2 = DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $tz = null);
$out = $dt->format(string $format): string;
$dt  = $dt->modify(string $modifier): DateTime|false;
$iv  = $dt->diff(DateTimeInterface $target, bool $absolute = false): DateInterval;

// 不変版
$di  = new DateTimeImmutable(string $datetime = "now", ?DateTimeZone $tz = null);
$di2 = $di->modify(string $modifier): DateTimeImmutable|false;

// 手続き型ラッパー(同等機能)
$d1 = date_create(string $datetime = "now", ?DateTimeZone $tz = null): DateTime|false;
$d2 = date_create_from_format(string $format, string $datetime, ?DateTimeZone $tz = null): DateTime|false;
$s  = date_format(DateTimeInterface $object, string $format): string;
$d1 = date_modify(DateTime $object, string $modifier): DateTime|false;
$iv = date_diff(DateTimeInterface $base, DateTimeInterface $target, bool $absolute = false): DateInterval;

引数(主要)

引数必須既定値説明
$datetimestring“now”“2025-08-31 09:00” / 相対表現 “next monday” 等
$formatstringY-m-d H:i:s / c / カスタム書式
$tzDateTimeZonenullnull
$modifierstring+1 day, -2 hours, first day of next month
$absoluteboolfalse差分の符号無視(絶対値)
  • 戻り値DateTime / DateTimeImmutable / DateInterval / string / false(関数系の失敗時)
  • 例外/副作用:不正なタイムゾーンなどで ExceptionDateTime破壊的(同インスタンスが変化)。既定タイムゾーンはグローバル設定に依存。

使用例

最小例

<?php
declare(strict_types=1);

date_default_timezone_set('Asia/Tokyo');

$now = new DateTime(); // 既定tz=Asia/Tokyo
echo $now->format('Y-m-d H:i:s'), PHP_EOL;

実務例(API受信 → パース → UTC正規化 → 期限チェック)

<?php
declare(strict_types=1);

use DateTime;
use DateTimeImmutable;
use DateTimeZone;
use Exception;

// 外部APIから "2025/08/31 14:30"(JST)で受領
$raw = '2025/08/31 14:30';
$jp  = new DateTimeZone('Asia/Tokyo');
$utc = new DateTimeZone('UTC');

try {
    // 固定フォーマットで堅牢にパース
    $dtJst = DateTime::createFromFormat('Y/m/d H:i', $raw, $jp);
    if ($dtJst === false) {
        throw new Exception('Datetime parse failed: '.$raw);
    }

    // UTCへ正規化して保存
    $dtUtc = (new DateTimeImmutable($dtJst->format('Y-m-d H:i:s'), $jp))
        ->setTimezone($utc);

    // 期限まで24時間を切ったら警告
    $deadlineUtc = $dtUtc->modify('+2 days');
    $nowUtc = (new DateTimeImmutable('now', $utc));
    $remain = $nowUtc->diff($deadlineUtc);

    if ($deadlineUtc < $nowUtc) {
        echo "期限切れ\n";
    } elseif ($remain->days === 0 && $remain->h < 24) {
        echo "期限が24時間以内です\n";
    }

    // 永続化用:ISO8601
    $store = $dtUtc->format(DateTime::ATOM); // 例: "2025-08-31T05:30:00+00:00"
    echo $store, PHP_EOL;
} catch (Exception $e) {
    // ログ&フォールバック
    error_log($e->getMessage());
    http_response_code(400);
}

よくある落とし穴・注意

  • 可変/不変DateTimemodify()自身が変わる。副作用回避は DateTimeImmutable を採用。
  • 既定タイムゾーン:未指定だと date_default_timezone_get() に依存(環境差異)。常に明示が安全。
  • パース失敗createFromFormat() は false を返すことがある。戻り値チェック必須
  • 夏時間/DSTAmerica/* などは存在しない時刻がある。UTC基準で保存→表示時にローカル変換が無難。
  • ミリ秒u(マイクロ秒)を使う。フォーマットと入力の桁を合わせる。

代替・関連APIとの比較

  • 手続き型関数(date_create系):簡潔だが false 監視が煩雑。大規模コードは OOP を推奨。
  • DateTime vs DateTimeImmutable:並行処理・ドメインロジックでは不変が安全(思わぬ共有変更を防止)。
  • Carbon(Laravel):リッチAPI(addDays(), isToday() 等)。学習コストと依存を考え、標準で足りる所は DateTime を優先。

テスト例(Pest)

<?php

use DateTimeImmutable;
use DateTimeZone;

it('parses JST and normalizes to UTC', function () {
    $jst = new DateTimeZone('Asia/Tokyo');
    $utc = new DateTimeZone('UTC');

    $dt = new DateTimeImmutable('2025-08-31 09:00:00', $jst);
    $utcStr = $dt->setTimezone($utc)->format('Y-m-d H:i:s');

    expect($utcStr)->toBe('2025-08-31 00:00:00'); // JST-9h
});

トラブルシュート(エラー別)

症状/エラー原因対処
Call to a member function format() on boolcreateFromFormat() 等が失敗し false戻り値の型を確認し、パース失敗時の分岐を追加
Failed to parse time string入力書式と $format が不一致フォーマットを見直し、DateTime::getLastErrors() で詳細確認
Unknown or bad timezoneタイムゾーンIDの誤りnew DateTimeZone('Asia/Tokyo') のように有効IDを使用
時差ずれ/1時間ズレDST・夏時間保存は UTC、表示時にローカルへ変換

参考リンク

レン (Wren)

こんにちは。レンです。

Laravelのコードの森に住んでいる、小さな案内役です。
ルーティングの枝やクラスの影を歩きながら、コードの流れや仕組みを眺めています。

このサイトでは、Laravelの基本から実装のコツまで、開発で役立つポイントを静かに整理しています。
難しいことを増やすのではなく、コードの見通しが少し良くなるヒントを届けるのが役目です。

「この処理はどこに書くのがいいのか」
「Laravelではどう考えると整理できるのか」

そんな疑問に、小さなメモを残すような気持ちで記事を書いています。

コードを書いている途中で迷ったとき、
このサイトが少し立ち止まって整理できる場所になればうれしいです。

レン (Wren)をフォローする