あっぽログ
← 記事一覧に戻る

PHPのDateTimeImmutableを使いこなす:日付・時刻操作の基本から実践まで

はじめに

PHPで日付や時刻を扱う場面は非常に多いです。ログの記録、予約システム、締め切り管理など、あらゆるアプリケーションで必要になります。

PHPには日付操作のためのクラスとして DateTimeDateTimeImmutable が用意されています。本記事では、バグを生みにくい安全な設計である 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(新しいオブジェクトが返る)

DateTimemodify() などのメソッドを呼ぶと元のオブジェクト自体が書き換わります。これは意図せぬバグの温床になります。

一方 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() メソッドに渡す書式文字を覚えておくと便利です。

書式説明
Y4桁の年2026
m2桁の月07
d2桁の日19
H24時間制の時10
i30
s00
N曜日(1=月〜7=日)7
UUnixタイムスタンプ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 を積極的に活用して、安全で読みやすいコードを書きましょう。

← 記事一覧に戻る