はじめに
PHPで日付や時刻を扱う場面は非常に多いです。ログの記録、予約システム、締め切り管理など、あらゆるアプリケーションで必要になります。
PHPには日付操作のためのクラスとして DateTime と DateTimeImmutable が用意されています。本記事では、バグを生みにくい安全な設計である DateTimeImmutable を中心に、日付・時刻操作の基本から実践的な使い方まで解説します。
DateTimeとDateTimeImmutableの違い
まず、2つのクラスの最大の違いを押さえましょう。
<?php
// DateTime は破壊的変更(元のオブジェクトが変わる)
$dt = new DateTime('2026-01-01');
$dt->modify('+7 days');
echo $dt->format('Y-m-d'); // 2026-01-08(元のオブジェクトが変わってしまう)
// DateTimeImmutable は非破壊的変更(元のオブジェクトはそのまま)
$dti = new DateTimeImmutable('2026-01-01');
$next = $dti->modify('+7 days');
echo $dti->format('Y-m-d'); // 2026-01-01(元は変わらない)
echo $next->format('Y-m-d'); // 2026-01-08(新しいオブジェクトが返る)
DateTime は modify() などのメソッドを呼ぶと元のオブジェクト自体が書き換わります。これは意図せぬバグの温床になります。
一方 DateTimeImmutable は常に新しいオブジェクトを返すため、元の値が予期せず変わる心配がありません。特別な理由がない限り DateTimeImmutable を使うことを推奨します。
基本的な使い方
オブジェクトの生成
<?php
// 現在日時
$now = new DateTimeImmutable();
echo $now->format('Y-m-d H:i:s'); // 例: 2026-07-19 10:30:00
// 特定の日時を指定
$date = new DateTimeImmutable('2026-12-31 23:59:59');
echo $date->format('Y-m-d'); // 2026-12-31
// 文字列から生成(createFromFormat)
$date2 = DateTimeImmutable::createFromFormat('d/m/Y', '31/12/2026');
echo $date2->format('Y-m-d'); // 2026-12-31
// Unixタイムスタンプから生成
$timestamp = new DateTimeImmutable('@1753000000');
echo $timestamp->format('Y-m-d H:i:s');
フォーマット指定一覧
format() メソッドに渡す書式文字を覚えておくと便利です。
| 書式 | 説明 | 例 |
|---|---|---|
Y | 4桁の年 | 2026 |
m | 2桁の月 | 07 |
d | 2桁の日 | 19 |
H | 24時間制の時 | 10 |
i | 分 | 30 |
s | 秒 | 00 |
N | 曜日(1=月〜7=日) | 7 |
U | Unixタイムスタンプ | 1753000000 |
日付の計算
DateIntervalを使った加減算
<?php
$today = new DateTimeImmutable('2026-07-19');
// 1ヶ月後
$nextMonth = $today->modify('+1 month');
echo $nextMonth->format('Y-m-d'); // 2026-08-19
// DateIntervalを使う方法
$interval = new DateInterval('P1Y2M3D'); // 1年2ヶ月3日
$future = $today->add($interval);
echo $future->format('Y-m-d'); // 2027-09-22
// 3週間前
$past = $today->sub(new DateInterval('P3W'));
echo $past->format('Y-m-d'); // 2026-06-28
DateInterval の書式は P で始まり、Y(年)・M(月)・D(日)・T(時刻区切り)・H(時)・M(分)・S(秒)を組み合わせます。
2つの日付の差を計算する
<?php
$start = new DateTimeImmutable('2026-01-01');
$end = new DateTimeImmutable('2026-07-19');
$diff = $start->diff($end);
echo $diff->days; // 総日数: 199
echo $diff->m; // 月の差: 6
echo $diff->d; // 端数の日: 18
// 期限切れチェック
$deadline = new DateTimeImmutable('2026-08-01');
$now = new DateTimeImmutable();
if ($now > $deadline) {
echo '期限切れです';
} else {
$remaining = $now->diff($deadline);
echo "あと {$remaining->days} 日です";
}
タイムゾーンの扱い
グローバルなサービスでは、タイムゾーンの正確な扱いが重要です。
<?php
// タイムゾーンを指定してオブジェクト生成
$tokyo = new DateTimeImmutable('now', new DateTimeZone('Asia/Tokyo'));
$utc = new DateTimeImmutable('now', new DateTimeZone('UTC'));
echo $tokyo->format('Y-m-d H:i:s T'); // 例: 2026-07-19 10:30:00 JST
echo $utc->format('Y-m-d H:i:s T'); // 例: 2026-07-19 01:30:00 UTC
// タイムゾーンを変換する(元のオブジェクトは変わらない)
$converted = $tokyo->setTimezone(new DateTimeZone('America/New_York'));
echo $converted->format('Y-m-d H:i:s T'); // ニューヨーク時刻に変換
実践例:有効期限チェック関数
実際のアプリケーションで使えるサンプルです。
<?php
/**
* トークンの有効期限を検証する
*/
function isTokenValid(string $issuedAt, int $ttlSeconds): bool
{
$issued = new DateTimeImmutable($issuedAt, new DateTimeZone('UTC'));
$expires = $issued->add(new DateInterval("PT{$ttlSeconds}S"));
$now = new DateTimeImmutable('now', new DateTimeZone('UTC'));
return $now <= $expires;
}
// 発行日時と有効期限(秒)を渡す
$valid = isTokenValid('2026-07-19 01:00:00', 3600); // 1時間有効
echo $valid ? '有効なトークン' : '期限切れトークン';
まとめ
本記事で紹介した内容を振り返ります。
DateTimeImmutableは元のオブジェクトを変えない安全な設計で、DateTimeより推奨createFromFormat()で任意フォーマットの文字列をパースできるmodify()/add()/sub()で日付計算が簡単に行えるdiff()で2つの日付の差をDateIntervalとして取得できるDateTimeZoneを組み合わせることでタイムゾーン対応も容易
日付・時刻の扱いはバグが生まれやすい領域のひとつです。DateTimeImmutable を積極的に活用して、安全で読みやすいコードを書きましょう。