- カテゴリ: 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;
引数(主要)
| 引数 | 型 | 必須 | 既定値 | 説明 |
|---|---|---|---|---|
| $datetime | string | “now” | “2025-08-31 09:00” / 相対表現 “next monday” 等 | |
| $format | string | ○ | Y-m-d H:i:s / c / カスタム書式 | |
| $tz | DateTimeZone | null | null | |
| $modifier | string | ○ | +1 day, -2 hours, first day of next month | |
| $absolute | bool | false | 差分の符号無視(絶対値) |
- 戻り値:
DateTime/DateTimeImmutable/DateInterval/string/false(関数系の失敗時) - 例外/副作用:不正なタイムゾーンなどで
Exception。DateTimeは 破壊的(同インスタンスが変化)。既定タイムゾーンはグローバル設定に依存。
使用例
最小例
<?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);
}
よくある落とし穴・注意
- 可変/不変:
DateTimeはmodify()で自身が変わる。副作用回避はDateTimeImmutableを採用。 - 既定タイムゾーン:未指定だと
date_default_timezone_get()に依存(環境差異)。常に明示が安全。 - パース失敗:
createFromFormat()は false を返すことがある。戻り値チェック必須。 - 夏時間/DST:
America/*などは存在しない時刻がある。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 bool | createFromFormat() 等が失敗し false | 戻り値の型を確認し、パース失敗時の分岐を追加 |
Failed to parse time string | 入力書式と $format が不一致 | フォーマットを見直し、DateTime::getLastErrors() で詳細確認 |
Unknown or bad timezone | タイムゾーンIDの誤り | new DateTimeZone('Asia/Tokyo') のように有効IDを使用 |
| 時差ずれ/1時間ズレ | DST・夏時間 | 保存は UTC、表示時にローカルへ変換 |
参考リンク
- PHP マニュアル — DateTime
https://www.php.net/manual/class.datetime.php - PHP マニュアル — DateTimeImmutable
https://www.php.net/manual/class.datetimeimmutable.php - PHP マニュアル — DateInterval
https://www.php.net/manual/class.dateinterval.php - PHP マニュアル — DateTimeZone
https://www.php.net/manual/class.datetimezone.php - PHP マニュアル — 日時の書式(
date()フォーマット文字)
https://www.php.net/manual/function.date.php

