Agent Grow Tech Notes

PHPのDateIntervalのことを真面目に調べてみた

2024/10/21に公開

はじめに

日時同士の差分を計算する処理を実装すること、ありますよね。
PHPではDateTime::diff()[1]を使って算出することで、DateIntervalクラスが生成されます。
ただ、このクラスのこと、イマイチよくわかっていなんじゃないかと、ワタクシ気づきました。

今回は、PHP以外の言語の日時差分の処理と見比べつつ、
PHPのDateIntervalのことを深く知っていこうと思います!

対象読者

  • PHPの基本的な言語知識がある

PHPのDateIntervalってそもそも何?

一言で書けば「日付の間隔」を表すクラスです。

https://www.php.net/manual/ja/class.dateinterval.php

公式の日本語訳にもバッチリ同じことを書いてますね。
はじめに書いた通り、基本的にはDateTime::diff()の判定結果として返却されるクラスです。

例えば、こんな使い方ですね。

<?php

$begin = new DateTime("2024-10-01 09:00:00");
$end = new DateTime("2024-11-15 18:00:00");

$diff = $end->diff($begin);

// 年月日時分秒でフォーマットした時
echo $diff->format("%Mヶ月%d日 %H時間%I分%S秒"). "\n";
// 合計何日間かを取得する時
echo $diff->days . "日" . "\n";

// 実行結果
// 01ヶ月14日 09時間00分00秒
// 45日

リファレンスを見た限り、年月日時分秒ときれいに出したいときはDateInterval::format[2]を使い、それ以外は、各種プロパティを参照することになりそう。

ただ「1ヶ月14日と9時間=合計何時間?」というのは、自力で算出する必要があるようです。
合計何日かを取得できるdaysプロパティ[3]は存在するのに、なんだか物足りない感じですね。

diffを使う以外でDateIntervalを生成するには?

diffメソッドの返却値として取り扱うことが大半な気がしますが、直接、DateIntervalを生成する方法が2つ用意されています。

コンストラクタを使う

https://www.php.net/manual/ja/dateinterval.construct.php
普通にコンストラクタが用意されてます。

さきほどの差分を生成しようとするとこんな感じ。

$diff = new DateInterval("P1M14DT9H");

引数で指定するのは、ISOの時間間隔の規格に沿ったものらしい。
PHPだと、日時の加算減算でよく目にする書式ですね。[4]

「DateInterval::createFromDateString」を使う

https://www.php.net/manual/ja/dateinterval.createfromdatestring.php
もう一つは、引数に別の書式が設定できるスタイルです。

こっちでコンストラクタと同じことをしようとするとこんな感じ。

$diff = DateInterval::createFromDateString("1 month + 14 days + 9 hours");

英単語って感じでわかりやすいような、プログラムで目にするには違和感があるような笑

こちらは、PHPが用意している相対的書式というやつを使う必要があります。
こちらも日時の生成や加算減算で見たことあるものですね。

他の言語だと、どんな感じ?

さて、ここからは私が開発経験のある他の言語について、同じような操作を比べてみましょう!

C#

using System;

public class DateTimeDiff {
    public static void Main(){
        DateTime begin = DateTime.Parse("2024-10-01 09:00:00");
        DateTime end = DateTime.Parse("2024-11-15 18:00:00");
        
        var span = end - begin;

        // 日付部より細かい単位しか書式が用意されていない
        Console.WriteLine(span.ToString(@"dd\日\ hh\時\間mm\分ss\秒"));
        // 合計日数以外も出せるよ
        Console.WriteLine(span.TotalDays + "日");
        Console.WriteLine(span.TotalHours + "時間");
        
        // 実行結果
        // 45日 09時間00分00秒
        // 45.375日
        // 1089時間
    }
}

https://learn.microsoft.com/ja-jp/dotnet/api/system.timespan

C#だと、DateIntevalとほぼ同等なクラスはTimeSpanというようです。
そもそもDateTime型同士を-の演算子を使って日時間隔を出せることにビビりました笑

ただ、PHPとは異なり、日数より大きい単位で算出する機能は用意していないようです。
「間隔が何ヶ月か?」を算出するには、別途処理を作らないとですね。

その一方で、日数より細かい単位については、TotalHoursのように合計いくつかを出すプロパティがそれぞれ用意されているので、これは便利!

Java

import java.util.*;
import java.time.*;
import java.time.format.*;

public class Main {
    public static void main(String[] args) throws Exception {
        
        var format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        var begin = LocalDateTime.parse("2024-10-01 09:00:00", format);
        var end = LocalDateTime.parse("2024-11-15 18:00:00", format);
        
        Duration duration = Duration.between(begin, end);
        
        // 書式化なメソッドは用意されてないよ
        // 合計日数以外も出せるよ
        System.out.println(duration.toDays() + "日");
        System.out.println(duration.toHours() + "時間");
        
        // 実行結果
        // 45日
        // 1089時間
    }
}

https://docs.oracle.com/javase/jp/8/docs/api/java/time/Duration.html

JavaだとDateIntervalに相当するのは、Durationというクラスでした。
こちらもC#と同じく、日数以上の大きな単位は用意されていないようです。
さらには、年月日時分秒な表記をすることもできないみたい。

日数の算出が、C#は小数点以下もあるのに対して、Javaは整数に丸められているのも違いますね。
PHPも丸められているので、C#がちょっとトリッキーなのかも。

そういえば、DurationはJava8から実装されたクラスのようなので、それ以前の日時間隔の算出がどれほど大変だったのか、想像したくないですね(遠い目)[5]

Python

もう一つくらい言語比較したいなってことで、開発経験少ないけど、Pythonも見てみます。[6]

from datetime import datetime, timedelta

format = '%Y-%m-%d %H:%M:%S'
begin = datetime.strptime('2024-10-01 09:00:00', format)
end = datetime.strptime('2024-11-15 18:00:00', format)

diff = end - begin

# 取得単位が一部しかない感じらしい
print(diff)
print(f"{diff.days}日")
print(f"{diff.seconds}秒")

# 実行結果
# 45 days, 9:00:00
# 45日
# 32400秒

https://docs.python.org/ja/3/library/datetime.html#datetime.timedelta

Pythonではtimedeltaが、DateIntervalに相当しそうです。
あんまり使う機会を想定されていないのか、年月日時分秒の書式もないし、合計の取得処理も一部の単位に限られているようです。
個人的には、PHPのほうがまだ使いやすい感じがしますね。

まとめ

PHPのDateIntervalを中心にいろんな言語の日時間隔の処理を見てみました。
言語ごとに微妙に挙動が違うようなので、久しぶりに触る言語で実装するときには気をつけたいですね。

調べる前は「PHPのDateIntervar使いにくいな~」と思ってましたが、いろいろ比較した結果、わりとPHPやるやんという結論になったので、非常に面白かったです!

脚注
  1. 厳密に言うと、DateTimeInterface::diff() ですが、普段意識するのは、DateTime型なので、この記事ではDateTimeで統一します。 ↩︎

  2. https://www.php.net/manual/ja/dateinterval.format.php ↩︎

  3. https://www.php.net/manual/ja/class.dateinterval.php#dateinterval.props.days ↩︎

  4. 毎回この書式わからなくなるの私だけ? ↩︎

  5. PHPも5.x系から実装されたようなので、同じだったかもと思いつつ ↩︎

  6. javascriptはライブラリ使わないとしんどい感じだったので ↩︎

Agent Grow Tech Notes
Agent Grow Tech Notes

Discussion