PHP の date_diff と日時の差分計算

2 つの日時の差を求める処理は、期限までの残り日数の表示や経過時間の計算など、さまざまな場面で必要になります。PHP では DateTime::diff メソッドと手続き型の date_diff 関数の 2 通りの方法で日時の差分を DateInterval オブジェクトとして取得できます。

diff メソッドと date_diff 関数

DateTime オブジェクトの diff メソッドを呼ぶ方法と、date_diff 関数に 2 つの DateTime を渡す方法は、結果が同じです。

$start = new DateTime('2025-01-01');
$end   = new DateTime('2025-07-15');

// オブジェクト指向スタイル
$diff1 = $start->diff($end);

// 手続き型スタイル
$diff2 = date_diff($start, $end);

echo $diff1->days; // 195
echo $diff2->days; // 195

どちらを使うかはプロジェクトのコーディングスタイル次第ですが、DateTime を使っている文脈であれば diff メソッドのほうが自然に読めるでしょう。

DateInterval のプロパティ

diff や date_diff が返す DateInterval オブジェクトには、差分を構成する各単位のプロパティが格納されています。

プロパティ意味
yint年数
mint月数(0〜11)
dint日数(0〜30)
プロパティ意味
hint時間数(0〜23)
iint分数(0〜59)
sint秒数(0〜59)

これらのプロパティは繰り上がり後の値を表しています。たとえば差が 1 年 3 か月 10 日なら、y が 1、m が 3、d が 10 となり、m に 15 が入ることはありません。

$start = new DateTime('2024-03-20');
$end   = new DateTime('2025-07-01');
$diff  = $start->diff($end);

echo $diff->y . '年' . $diff->m . 'か月' . $diff->d . '日';
// 1年3か月11日

days プロパティで通算日数を取得する

個別のプロパティとは別に、days プロパティは差分の通算日数を整数で返します。

$start = new DateTime('2025-01-01');
$end   = new DateTime('2025-12-31');
$diff  = $start->diff($end);

echo $diff->days . '日間';
// 364日間

「あと何日」「何日経過」のように日数だけが必要な場面では、y・m・d を組み合わせるよりも days を使うほうがシンプルです。

y・m・d を使う場合

「1 年 3 か月 11 日」のように人間が読みやすい形式で差分を表現したいときに適している。

days を使う場合

「195 日間」のように通算の日数だけを知りたいときに適している。カウントダウンや日数計算に向いている。

invert プロパティで方向を判定する

diff は引数の順序によって差分の方向が変わります。invert プロパティが 0 なら正(end より前)、1 なら負(end より後)を意味します。

$a = new DateTime('2025-07-01');
$b = new DateTime('2025-01-01');

$diff = $a->diff($b);
echo $diff->invert; // 1($aが$bより後なので負方向)
echo $diff->days;   // 181

days やその他のプロパティは常に正の値を返すため、「前後どちらか」を判定するには invert を確認する必要があります。

$start = new DateTime('2025-01-01');
$end   = new DateTime('2025-07-01');
$diff  = $start->diff($end);

if ($diff->invert === 0) {
    echo $diff->days . '日後';
} else {
    echo $diff->days . '日前';
}
// 181日後

format メソッドで差分を整形する

DateInterval の format メソッドを使うと、差分を任意の書式で文字列に変換できます。

$start = new DateTime('2025-01-01 08:00:00');
$end   = new DateTime('2025-07-15 14:30:45');
$diff  = $start->diff($end);

echo $diff->format('%y年%mか月%d日 %h時間%i分%s秒');
// 0年6か月14日 6時間30分45秒

フォーマット文字は % に続けて指定します。

文字意味
%y年(0 埋めなし)0, 1, 2
%m月(0 埋めなし)0〜11
%d日(0 埋めなし)0〜30
文字意味
%h時間(0 埋めなし)0〜23
%i分(0 埋めなし)0〜59
%a通算日数195

0 埋めしたい場合は大文字を使います。%Y は 0 埋め年、%M は 0 埋め月、%D は 0 埋め日を返します。

$diff = (new DateTime('2025-01-01'))->diff(new DateTime('2025-02-05'));
echo $diff->format('%M月%D日');
// 01月04日

実用例:年齢を計算する

生年月日から現在の年齢を求める処理は diff の典型的な用途です。

function calculateAge(string $birthday): int
{
    $birth = new DateTime($birthday);
    $today = new DateTime('today');
    return $birth->diff($today)->y;
}

echo calculateAge('1990-05-20');
// 例: 35

diff が返す DateInterval の y プロパティには繰り上がり後の年数が入るため、誕生月日を迎えたかどうかも自動的に考慮されます。基準を「today」にすることで時刻部分が 0:00:00 にリセットされ、時刻による誤差も生じません。

実用例:締め切りまでの残り時間を表示する

$deadline = new DateTime('2025-12-31 23:59:59');
$now      = new DateTime();
$diff     = $now->diff($deadline);

if ($diff->invert === 1) {
    echo '締め切りを過ぎています';
} else {
    echo '残り ' . $diff->format('%a日 %h時間%i分');
}
// 例: 残り 199日 9時間29分

invert で期限を過ぎたかどうかを判定し、format で残り時間を人間が読みやすい形式にしています。days ではなく %a を使っているのは、format メソッドの中で通算日数を参照するためです。