LoginSignup
41
42

More than 3 years have passed since last update.

PHPの日付・時刻関連をまとめてみた

Last updated at Posted at 2019-01-19

目新しいものは何もないし、何番煎じだよという感じだし、そもそも公式マニュアルを読めば分かることだらけだけど、1つのページにまとまっていることが大事なんだと思うんだ(自己暗示)

はじめに

現在、PHP には、date()strtotime() をはじめとする関数と、PHP 5.2 から追加された DateTime クラスの2種類が存在します。しかし、DateTime クラスはまだ十分普及しているとは言えないようにも見えます。1

そこで、この記事では、DateTime クラスの使い方について紹介したいと思います。また、既存のソースコードを読むときのことを考えて、date() をはじめとする関数の使い方についても書いておきたいと思います。

比較表

既存の関数が DateTime クラスだとどのメソッドに相当するのかを一目でわかったほうが取っ付きやすくていいのではないでしょうか。

ということで、比較表を作ってみました。あくまでも 機能が似ている というだけで、同じ動作をする というわけではありません。

用途 関数 DateTime クラス
現在日時 date() __construct()
日時整形 date(書式文字列) format(書式文字列)
タイムゾーン date_default_timezone_set(タイムゾーン) setTimezone(タイムゾーン)
日時指定(文字列) strtotime(文字列) __construct(文字列),modify(文字列)
日時指定(数値) mktime(時, 分, 秒, 月, 日, 年) setDate(年, 月, 日), setTime(時, 分, 秒)
日時加減算 (該当なし) modify(文字列), add(間隔),sub(間隔)
差分 (該当なし) diff(比較対象)

DateTimeImmutable

DateTime クラスを実際に使いはじめる前に、知っておかなければならないことがあります。

例えば、DateTime クラスを使って以下のように書いたとします。

// 現在日時
$now = new DateTime();
$now->format("Y-m-d H:i:s");

// 現在日時の1日後
$tomorrow = $now->modify("+1 day");
$tomorrow->format("Y-m-d H:i:s");

// 現在日時の1ヶ月後(のつもり)
$nextMonth = $now->modify("+1 month");
$nextMonth->format("Y-m-d H:i:s");

これを実行すると、以下のような結果になります。

// 現在日時
2019-01-17 13:02:40

// 現在日時の1日後
2019-01-18 13:02:40

// 現在日時の1ヶ月後・・・じゃない!
2019-02-18 13:02:40

$now->modify("+1 month") と書くことで、現在日時の 1ヶ月後を計算したつもりでした。しかし、実際には 現在日時の1日後の 1ヶ月後となってしまいました。

これは、DateTime クラスが ミュータブル (変更可能)なクラスであることが原因です。

例えば、$tomorrow = $now->modify("+1 day") と書いたとき、実は $tomorrow に格納される日時だけでなく、$now に格納されている日時まで変更されてしまうのです。

一方、イミュータブル (変更不可能)なクラスとして DateTimeImmutable クラスが用意されています。これを使うと、先程のソースコードは以下のようになります。

// 現在日時
// クラスを DateTime から DateTimeImmutable へ変更
$now = new DateTimeImmutable();
$now->format("Y-m-d H:i:s");

// 現在日時の1日後
$tomorrow = $now->modify("+1 day");
$tomorrow->format("Y-m-d H:i:s");

// 現在日時の1ヶ月後
$nextMonth = $now->modify("+1 month");
$nextMonth->format("Y-m-d H:i:s");
// 現在日時
2019-01-17 13:02:40

// 現在日時の1日後
2019-01-18 13:02:40

// 現在日時の1ヶ月後になっている!
2019-02-17 13:02:40

無用な混乱を避けるためにも、なるべく DateTimeImmutable クラスを使うべきでしょう。

以下のコード例では全て DateTimeImmutable クラスを使います。ただし、スペルが長いので、説明文では DateTime と書くことにします(両者の違いは ミュータブルイミュータブル かだけです)。

現在日時

DateTime

コンストラクターの第1引数に何も指定しない場合は、現在日時になります。
また、format() メソッドで、引数に指定した書式文字列で整形しています。

$date = new DateTimeImmutable();
$date->format("Y-m-d H:i:s");    // 2019-01-17 13:44:18

date()

date() 関数の第2引数に何も指定しない場合は、現在日時になります。
また、第1引数に指定した書式文字列で整形しています。

date("Y-m-d H:i:s");             // 2019-01-17 13:44:18

書式文字列

DateTime::format() メソッドと date() 関数で引数に指定できる書式文字列については、date() 関数の マニュアル を参照してください。

代表的なものは以下のとおりです。

書式文字列 説明
d 日(数値2桁、先頭0埋めする) 01 から 31
j 日(数値1〜2桁、先頭0埋めしない) 1 から 31
D 曜日名(英語略称) Mon から Sun
l (小文字の 'L') 曜日名(英語名称) Sunday から Saturday
w 曜日(数値1桁) 0 (日曜)から 6 (土曜)
m 月(数値2桁、先頭0埋めする) 01 から 12
n 月(数値1〜2桁、先頭0埋めしない) 1 から 12
M 月名(英語略称) Jan から Dec
F 月名(英語名称) January から December
Y 年(数値4桁) 例: 1999 または 2003
y 年(数値2桁) 例: 99 または 03
a 午前または午後(小文字) am または pm
A 午前または午後(大文字) AM または PM
g 時(12時間単位、数値1〜2桁、先頭0埋めしない) 1 から 12
G 時(24時間単位、数値1〜2桁、先頭0埋めしない) 0 から 23
h 時(12時間単位、数値2桁、先頭0埋めする) 01 から 12
H 時(24時間単位、数値2桁、先頭0埋めする) 00 から 23
i 分(数値2桁、先頭0埋めする) 00 から 59
s 秒(数値2桁、先頭0埋めする) 00 から 59
u マイクロ秒(PHP 5.2.2 以降)。 date() 関数では常に 000000 となる。DateTime クラスでは正常に表示される。 例: 654321
v ミリ秒(PHP 7.0.0 以降)。date() 関数では常に 000 となる。DateTime クラスでは正常に表示される。 例: 654

日本語を書式文字列に含めることもできます。

$date = new DateTimeImmutable();
$date->format("Y年n月j日 H時i分s秒");     // 2019年1月17日 21時16分02秒

書式文字列 u でマイクロ秒を表示することができますが、date() では表示できないことに注意しましょう。

// DateTime クラスではマイクロ秒も表示可能
$date = new DateTimeImmutable();
$date->format("Y-m-d H:i:s.u");         // 2019-01-17 21:16:02.285594

// date() ではマイクロ秒は "000000" になってしまう
date("Y-m-d H:i:s.u");                  // 2019-01-17 21:16:02.000000

書式指定 w は曜日を数値で表します。日本語で曜日を表示したいときに便利です。

$weekdays = ["日", "月", "火", "水", "木", "金", "土"];
$date = new DateTimeImmutable();
$dayOfWeek = $date->format("w");
echo "今日は" . $weekdays[$dayOfWeek] . "曜日です。\n";
// --> 今日は木曜日です。

タイムゾーン

DateTime クラスや date() 関数を使う際には、タイムゾーンを適切に設定しておく必要があります。
基本的には php.inidate.timezone でタイムゾーンを設定し、必要に応じて DateTime::setTimezone() メソッドや date_default_timezone_set() 関数を使います。2

設定できるタイムゾーンについては、サポートされるタイムゾーンのリスト を参照してください。

php.ini

以下のように date.timezone が設定されていない場合には、協定世界時 (UTC)が使われます。

[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
;date.timezone =

日本標準時にする場合は "Asia/Tokyo" とします。

[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = "Asia/Tokyo"

DateTime::setTimezone()

setTimezone() メソッドの引数にタイムゾーンを指定することができます。
また、コンストラクターの第2引数にタイムゾーンを指定することもできます。

// setTimezone() メソッドで米国東部標準時を指定する場合
$date = new DateTimeImmutable();
$date->setTimezone(new DateTimeZone("America/New_York"));
$date->format("Y-m-d H:i:s");       // 2019-01-18 09:17:09

// コンストラクターで米国東部標準時を指定する場合
$date = new DateTimeImmutable(NULL, new DateTimeZone("America/New_York"));
$date->format("Y-m-d H:i:s");       // 2019-01-18 09:17:09

date_default_timezone_set()

date_default_timezone_set() 関数の引数にタイムゾーンを指定することができます。

date_default_timezone_set("America/New_York");
date("Y-m-d H:i:s");                // 2019-01-18 09:17:09

日時指定

その1

英文形式の文字列を使って日時を指定する方法です。

DateTime

コンストラクター

コンストラクターの引数に文字列を指定することができます。

// 2月1日 7時00分30秒
$date = new DateTimeImmutable("2019/2/1 7:00:30");
$date->format("Y-m-d H:i:s");
// --> 2019-02-01 07:00:30

// 一番近い未来の月曜日 9時30分
$date = new DateTimeImmutable("Monday 9:30");
$date->format("Y-m-d H:i:s");
// --> 2019-01-21 09:30:00

// 翌日 10時05分
$date = new DateTimeImmutable("+1 day 10:05");
$date->format("Y-m-d H:i:s");
// --> 2019-01-20 10:05:00

modify()

DateTime オブジェクトの日時をあとから変更する場合は、modify() メソッドを使うことができます。

// 2月1日 7時00分30秒
$date = new DateTimeImmutable();
$date->modify("2019/2/1 7:00:30")->format("Y-m-d H:i:s");
// --> 2019-02-01 07:00:30

// 一番近い未来の月曜日 9時30分
$date = new DateTimeImmutable();
$date->modify("Monday 9:30")->format("Y-m-d H:i:s");
// --> 2019-01-21 09:30:00

// 翌日 10時05分
$date = new DateTimeImmutable();
$date->modify("Monday 9:30")->format("Y-m-d H:i:s");
// --> 2019-01-20 10:05:00

strtotime()

strtotime() 関数を使って、英文形式の文字列を UNIX タイムスタンプへ変換します。
変換後のタイムスタンプを date() 関数の第2引数に指定します。

date("Y-m-d H:i:s", strtotime("2019/2/1 7:00:30"));
// --> 2019-02-01 07:00:30

date("Y-m-d H:i:s", strtotime("Monday 9:30"));
// --> 2019-01-21 09:30:00

date("Y-m-d H:i:s", strtotime("+1 day  10:05"));
// --> 2019-01-20 10:05:00

書式

DateTime クラスのコンストラクター、modify() メソッド、そして strtotime() で指定できる書式は共通しています。詳しくは、サポートする日付と時刻の書式 を参照してください。

2019年1月20日 12:59:00 を基準にして、それぞれの書式を指定したときにどのような日時になるかを以下にまとめました。

文字列 説明
+1 year 1年後 2020-01-20 12:59:00
-1 year 1年前 2018-01-20 12:59:00
+1 week 1週間後 2019-01-27 12:59:00
-1 week 1週間前 2019-01-13 12:59:00
next Monday 一番近い未来の月曜日(時刻は00:00固定) 2019-01-21 00:00:00
last Monday 一番近い過去の月曜日(時刻は00:00固定) 2019-01-14 00:00:00
tomorrow 1日後(時刻は00:00固定) 2019-01-21 00:00:00
yesterday 1日前(時刻は00:00固定) 2019-01-19 00:00:00
+1 day 1日後 2019-01-21 12:59:00
-1 day 1日前 2019-01-19 12:59:00
+1 hour 1時間後 2019-01-20 13:59:00
-1 hour 1時間前 2019-01-20 11:59:00
first day of this month 月の最初の日 2019-01-01 11:59:00
last day of this month 月の最後の日 2019-01-31 12:59:00

その2

日時の情報を引数に渡すことで、日時を指定する方法です。

DateTime::setDate()、setTime()

setDate() メソッドで 年月日 を、setTime() メソッドで 時分秒 をそれぞれ指定します。

$date = new DateTimeImmutable();
$lastday = $date->setDate(2019, 1, 31)->setTime(12, 59, 0);
$lastday->format("Y-m-d H:i:s");
// --> 2019-01-31 12:59:00

mktime()

mktime() 関数を使って、日時情報を UNIX タイムスタンプへ変換します。
変換後のタイムスタンプを date() 関数の第2引数に指定します。

// mktime()の引数の順番は以下のとおり:
// 時, 分, 秒, 月, 日, 年
date("Y-m-d H:i:s", mktime(12, 59, 0, 1, 31, 2019)); // 2019-01-31 12:59:00

その3

DateTime::add()、sub()

間隔を表す DateInterval オブジェクトを作成し、DateTime::add() または DateTime::sub() に指定することで、日時の加減算を行う方法です。

日時を加算するときは add() メソッドを、減算するときは sub() メソッドをそれぞれ使います。

書式については DateInterval クラスのコンストラクターの マニュアル を参照してください。最初が P からはじまることと、日付と時間のあいだに T を置く必要があることに注意が必要です。3

以下に 2019年1月20日 12:59:00 を基準にしたときに、それぞれどのような日時になるかを示します。

$date = new DateTimeImmutable("2019/1/20 12:59:00");

// 1年後
$date->add(new DateInterval("P1Y"))->format("Y-m-d H:i:s");
// --> 2020-01-20 12:59:00

// 3週間前
$date->sub(new DateInterval("P3W"))->format("Y-m-d H:i:s");
// --> 2018-12-30 12:59:00

// 1日12時間後
$date->add(new DateInterval("P1DT12H"))->format("Y-m-d H:i:s");
// --> 2019-01-22 00:59:00

// 1時間30分前
$date->sub(new DateInterval("PT1H30M"))->format("Y-m-d H:i:s");
// --> 2019-01-20 11:29:00

// 5分30秒後
$date->add(new DateInterval("PT5M30S"))->format("Y-m-d H:i:s");
// --> 2019-01-20 13:04:30

注意事項

以下のように、1ヶ月後や1ヶ月前の日付を指定しようとすると、期待通りの結果にならないことがあります。

詳しくは、別記事 PHPで1ヶ月前や1ヶ月後の日付を求めるには を参照してください。

$date = new DateTimeImmutable("2019/1/31 10:00");
$date->modify("+1 months")->format("Y-m-d H:i:s");
// --> 2019-03-03 10:00:00

日時比較

比較演算子

DateTime オブジェクトは比較演算子(宇宙船演算子を除く)で比較することができます。
また、等しいことを確認するときは === 演算子ではなく == 演算子を使います。

$date1 = new DateTimeImmutable("2019-01-20 10:00:00");
$date2 = new DateTimeImmutable("2019-01-20 10:00:00");
$date3 = new DateTimeImmutable("2019-01-21 12:00:00");

var_dump($date1 == $date2);  // bool(true)
var_dump($date1 != $date2);  // bool(false)

// ===演算子はfalseになる
var_dump($date1 === $date2); // bool(false)

var_dump($date1 < $date2);   // bool(false)
var_dump($date1 > $date2);   // bool(false)
var_dump($date1 <= $date2);  // bool(true)
var_dump($date1 >= $date2);  // bool(true)

// 宇宙船演算子を使うとエラーになる
//var_dump($data1 <=> $data2); 


var_dump($date1 == $date3);  // bool(false)
var_dump($date1 != $date3);  // bool(true)
var_dump($date1 === $date3); // bool(false)
var_dump($date1 < $date3);   // bool(true)
var_dump($date1 > $date3);   // bool(false)
var_dump($date1 <= $date3);  // bool(true)
var_dump($date1 >= $date3);  // bool(false)


// 同一インスタンスを参照している場合
// ===演算子はtrueになる
$date4 = $date1;
var_dump($date4 === $date1); // bool(true)

DateTime::diff()

DateTime::diff() を使って、日時の差を計算することができます。
DateTime::diff()DateInterval オブジェクトを返すので、DateInterval::format() を使って整形します。

DateInterval::format() の引数に指定できる書式文字列については、マニュアル を参照してください。

例えば、東京オリンピック開催日までの日数を計算する場合は、以下のようになります。

$today = new DateTimeImmutable("2019-1-20");
$olymicStartDay = new DateTimeImmutable("2020-7-24");

// オリンピック開催日までの日数
// %a(総日数)を使っていることに注意
$interval = $today->diff($olymicStartDay);
print $interval->format("オリンピックまで あと %a 日") . "\n";
オリンピックまで あと 551 日

ここで書式文字列に %a (総日数)を指定していることに注意してください。
%d (日)を指定した場合は、2019年1月20日 の「日」( 20 )と 2020年7月24日の「日」(24)の差を計算して、4 という結果が返ってきてしまいます。

その他の書式文字列の使い方は、以下のようになります。

$today = new DateTimeImmutable("2019/1/20 18:00:00");
$future = new DateTimeImmutable("2019/2/22 19:30:45");
print $today->format("Y-m-d H:i:s") . "\n";
print $future->format("Y-m-d H:i:s") . "\n";
print $today->diff($future)->format("間隔: %R%y年%m月%d日%h時%i分%s秒");
2019-01-20 18:00:00
2019-02-22 19:30:45
間隔: +0年1月2日1時30分45秒

参考

最後に

今まで DateTime クラスを理解していなかった(使うこともなかった)のですが、良い勉強になりました。

更新履歴

  • 2019/1/20
    • 参考リンク追加
  • 2020/8/6
    • 書式文字列 v について修正(編集リクエストありがとうございました)

  1. 自分の観測範囲が古すぎるだけかもしれません。ただ、私はまだ業務で DateTime クラスを使っているプログラムを見たことがありません。 

  2. タイムゾーン設定の優先順位については date_default_timezone_get()マニュアル を参照。 

  3. M (Month) と (Minute)の両方で使われています。全体的に見づらいですが、ISO 規格らしいので仕方ありませんね・・・ 

41
42
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
42