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 オブジェクトには、差分を構成する各単位のプロパティが格納されています。
| プロパティ | 型 | 意味 |
|---|---|---|
| y | int | 年数 |
| m | int | 月数(0〜11) |
| d | int | 日数(0〜30) |
| プロパティ | 型 | 意味 |
|---|---|---|
| h | int | 時間数(0〜23) |
| i | int | 分数(0〜59) |
| s | int | 秒数(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 を使うほうがシンプルです。
「1 年 3 か月 11 日」のように人間が読みやすい形式で差分を表現したいときに適している。
「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; // 181days やその他のプロパティは常に正の値を返すため、「前後どちらか」を判定するには 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');
// 例: 35diff が返す 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 メソッドの中で通算日数を参照するためです。