PHP5.2.0以降では日時を扱うクラスとしてDateTimeクラスがあります。古くからある方法だとdate()関数もありますが、今回は比較的新しいDateTimeクラスの使い方を説明したいと思います。
DateTimeクラスの基本
こちらはクラスのコンストラクタ。
public __construct ([ string
$time
= "now" [, DateTimeZone$timezone
= NULL]] )
string型を引数に取り、初期値は"now"であることが分かります。タイムゾーンをここで設定することもできますが、この記事では触れません。
まずは現在日時を取得してみましょう。
現在日時を取得する
$now=new DateTime();
DateTime型のオブジェクトを引数無しで生成すると、現在時刻が自動的に入ります。
ただDateTime型のままでは画面に出力できないので、format()メソッドを使って整形したものを表示するようにします。
$now=new DateTime();
echo $now->format("Y/m/d H:i:s");
// 結果 : 2019/05/09 16:29:05
format()に渡す引数は書式文字列になりますが、こちらは後述しようと思います。
これで現在日時が取得できました。
文字列からDateTimeオブジェクトを生成
今度は日時を指定してDateTime型のインスタンスを生成してみましょう。
$now=new DateTime("2019/05/09 16:29:05");
引数に文字列で日時を指定。書式はある程度自由に設定できます。
$now2=new DateTime("2019-5-09 16:29:05");
$now3=new DateTime("2019/5/9 16.29.5");
上記のようにデリミタ(区切り文字。ここでは"/"(スラッシュ)や"."(ドット))を変更したり(2バイト文字はNG)、月や日、時間が1桁の場合の十の位の0を省略しても同じ結果が得られますが、19/5/9など、西暦の上2桁を省略してしまうとエラーになります。
DateTimeオブジェクトの年月日/時分秒を修正する
DateTimeオブジェクトの値を修正するにはsetDate()やsetTime()メソッドを使います。
$now=new DateTime();
$now->setDate(2018,3,20);
$now->setTime(10,0,0);
echo $now->format("Y/m/d H:i:s");
// 結果 : 2018/03/20 10:00:00
こちらのメソッドでは引数をint型にしていることに注意してください。,(カンマ)も引数を分けるカンマのため変更できません。
ちなみにsetTime()の秒は省略することができ、setTime(10,0)でも同じ結果となります。
DateTimeオブジェクトの加算/減算
既に生成されたDateTimeオブジェクトの値を加算/減算するにはmodify()メソッドが直感的に使えていいと思います。 上記の$nowの2日後を出力してみましょう。
$now->modify("+2days");
echo $now->format("Y/m/d H:i:s");
// 結果 : 2018/03/22 10:00:00
そこから1年と238日後は
$now->modify("+1year 238days");
echo $now->format("Y/m/d H:i:s");
// 結果 : 2019/11/13 10:00:00
その他"1week"や"30seconds"など、ほぼ英語で指定することができます。
format()のパラメータ文字列を指定する
DateTimeクラスのformat()メソッドでは引数としてパラメータ文字列を指定することで色々な日時表記方法に対応します。ただいかんせん数が多いので、ここでは使用頻度が高そうなものを抜粋しています。
全て知りたい方はこちらを参照してください。
https://php.net/manual/ja/function.date.php
文字 | 概要 | 値 |
---|---|---|
Y | 年 | 2019 |
y | 年(2桁表示) | 19 |
m | 月 | 01~12 |
d | 日 | 01~31 |
H | 時 | 01~23 |
h | 時(12時間表記) | 01~12 |
i | 分 | 00~59 |
s | 秒 | 00~59 |
l(小文字のL) | 曜日(英語表記) | Monday~Sunday |
F | 月(英語表記) | January~December |
a | 午前/午後 | am | pm |
それでは今日の日付 2019年5月9日 午後18時50分をインスタンス化して少し試してみましょう。
$today=new DateTime("2019/05/09 18:50:00");
echo $today->format("y年m月d日 h:ia");
// 結果 : 19年05月09日 06:50pm
echo $today->format("d F Y l");
// 結果 : 09 May 2019 Thursday
文字列の中には2バイト文字も問題無く使えます。逆に英語はパラメータ文字列と被ってしまう危険があるので使いにくいかもしれません。
DateTime型同士の差を計算する
2つのDateTime型を比較する場合、DateTime型のdiff()メソッドを使います。
diff()の戻り値は日時の間隔を表すDateIntervalオブジェクトになり、こちらのオブジェクトも出力する際はフォーマットが必要になります。
$dt1=new DateTime("2019/05/09 18:50:00");
$dt2=new DateTime("2017/08/30 12:30:00");
// 2つのDateTimeオブジェクトを用意
$diff=$dt1->diff($dt2);
// 片方のDateTimeオブジェクトのdiff()を使ってもう片方と比較する
// DateIntervalオブジェクトを変数に入れる
echo $diff->format("%y年%mヶ月%d日 %h時間%i分%s秒");
// DateIntervalオブジェクトをformatして表示
// 結果 : 1年8ヶ月10日 6時間20分0秒
DateInterval型のフォーマット
DateTime型とは異なるフォーマットでこんがらがりそうですが、こちらはパラメータの前に%を付けることだけ覚えていればそう難しくはないはず。
文字 | 概要 |
---|---|
%y | 年 |
%m | 月 |
%d | 日 |
%h | 時 |
%i | 分 |
%s | 秒 |
%a | 総日数 |
%r | 結果がマイナスのとき、-を表示 |
総日数以外は大文字もあります。結果が1桁のとき、頭の0を表示したいときは大文字にしてください。
なお%rの大文字は「+」も表示します。
コレを踏まえて、先ほどの結果表示を少し変えてみましょう。
echo $diff->format("%R%y年%mヶ月%d日 %H時間%i分%S秒 総日数は%a日");
// 結果 : -1年8ヶ月10日 06時間20分0秒 総日数は617日
DateTimeImmutableクラス
ここまで紹介してきたDateTimeクラスはミュータブル(可変)オブジェクトで、例えばDateTimeクラスのインスタンスメソッドmodify()を使えばそのオブジェクト自体の値が書き換えられます。
もしある日(以下の例では変数$dt1)を起点にして複数回の計算をしたいような場合、これでは非常にマズいことが起こります。
$dt1=new DateTime("2019/05/09");
$dt1->modify("+1day");
echo $dt1->format("Y/m/d");
// 結果 : 2019/05/10
$dt1->modify("+3days");
echo $dt1->format("Y/m/d");
// 結果 : 2019/05/13 (←5/9から3日後を出したいのに4日後が出てしまう)
ある日(5/9)を起点にして1日後と3日後の日付を出力しようとした場合、1回目のmodify()の後に続けて計算してしまうとmodify()によって$dt1自体の値が+1dayされてしまい、起点が書き換わってしまうため2回目の計算は意図したものと違うという現象が起こります。
起点である日時を意図せず変更されたくないのであればDateTimeクラスではなくDateTimeImmutableクラスとして生成することをオススメします。
DateTimeImmutableはDateTimeとほぼ同じですが、イミュータブル(変更不可)オブジェクトになります。
$dt1=new DateTimeImmutable("2019/05/09");
$dt1->modify("+3days");
echo $dt1->format("Y/m/d");
// 結果 : 2019/05/12
$dt2=$dt1->modify("+15days");
//新しい値が入るのであれば新しい変数に入れる
echo $dt2->format("Y/m/d");
// 結果 : 2019/05/24
値を変更したくない起点の日時をDateTimeImmutableクラスにすることでmodify()しても値が変わらないため、より安全な運用ができるはず。