PHPでの日時の扱い

日時を扱うたびに「あれ、どうしたっけ?」とネット検索したり過去ソースをひっくり返したりするのでメモにまとめました。

1. date関数 — Unixタイムスタンプを書式化する

https://www.php.net/manual/ja/function.date.php

1-1. 書式

date(string $format, ?int $timestamp = null): string

1-2. パラメタ

使うのだけ抜粋。他のパラメタはhttps://www.php.net/manual/ja/datetime.format.php


$format
  //年 -------------------------------
  L 閏年であるかどうか 1なら閏年。0なら閏年ではない。
  Y 4 桁の数字
  y 2 桁の数字
  //月  -------------------------------
  F 'January' 〜 'December'
  M 'Jan 〜 Dec'
  m 01 〜 12
  n 1 〜 12
  t 指定した月の日数(末日)
  //日 -------------------------------
  d 01 〜 31
  j 1 〜 31
  z 0 〜 365
  //曜日 -----------------------------
  D 'Mon' 〜 'Sun'
  N 1(月)〜 7(日)
  w 0(日)〜 6(土)
  //時 -------------------------------
  a 'am' / 'pm'
  A 'AM' / 'PM'
  g 12時 1 〜 12
  h 12時 01 〜 12
  G 24時 0 〜 23
  H 24時 00 〜 23
  //分  -------------------------------
  i 00 〜 59
  //秒  -------------------------------
  s 00 〜 59
  //タイムゾーン  ---------------------
  e タイムゾーン 例:UTC, GMT, Atlantic/Azores
  O GMTとの時差 例: +0200
  P GMTとの時差 例: +02:00
  Z タイムゾーンのオフセット秒数 -43200 〜 50400

$timestamp
  integer型の Unixタイムスタンプ
  省略時のデフォルト値はtime()の返り値(現在日時)となる

1-3. 使用例

$ php -v
PHP 8.2.12 (cli) (built: Nov  5 2023 21:48:15) (NTS)
  :
$ php -a
Interactive shell

php > date_default_timezone_set('Asia/Tokyo');
php >
php > $weekAry = array('日', '月', '火', '水', '木', '金', '土');
php > $ts = strtotime('2039/08/07');
php >
php > echo date('Y/m/d', $ts). '('. $weekAry[date('w', $ts)]. ')';
2039/08/07()
php > echo date('Y/m', $ts). 'の末日は'. date('t', $ts). '日です。';
2039/08の末日は31日です。

2. strtotime — 日付文字列を Unixタイムスタンプに変換する

https://www.php.net/manual/ja/function.strtotime.php

2-1. 書式

strtotime(string $datetime, ?int $baseTimestamp = null): int|false

2-2. パラメタ

使うのだけ抜粋。他のパラメタはhttps://www.php.net/manual/ja/datetime.formats.php


$datetime
  //日  -------------------------------
  8桁の数字 '20150505'
  '/'区切り '2015/05/05' or '2015/5/5'
  '-'区切り '2015-05-01' or '2015-5-1'
  //時  -------------------------------
  6桁の数字 '191919'
  ':'区切り '19:19:19'
  //日時  -----------------------------
  '/:'区切り '2015/05/05 19:19:19'
  //相対形式  -------------------------
  昨日 '-1 day'   //-24時間
  昨日 'yesterday' //前日 00:00:00
  明日 '+1 day'   //+24時間
  明日 'tomorrow'  //翌日 00:00:00
  ...etc

「来月の末日(last day of next month)」や「来週の月曜日の日付(Monday next week)」とかできる。

$baseTimestamp
  相対形式で使用されるタイムスタンプ。
  省略時のデフォルト値はtime()の返り値(現在日時)となる

2-3. 使用例

<?php
//タイムゾーン
date_default_timezone_set('Asia/Tokyo');

//日
$val["strtotime('now')"]        = strtotime('now');
$val["strtotime('+2 day')"]     = strtotime('+2 day');
$val["strtotime('2 day')"]      = strtotime('2 day');
$val["strtotime('-2 day')"]     = strtotime('-2 day');
$val["strtotime('2015/08/01')"] = strtotime('2015/08/01');
$val["strtotime('2039/08/01')"] = strtotime('2039/08/01');
$val["strtotime('yesterday')"]  = strtotime('yesterday');
$val["strtotime('tomorrow')"]   = strtotime('tomorrow');
$val["strtotime('yesterday 09:00:00')"] = strtotime('yesterday 09:00:00');

//週
$val["strtotime('+3 week')"] = strtotime('+3 week');
$val["strtotime('-3 week')"] = strtotime('-3 week');

//曜日
$val["strtotime('next sunday')"] = strtotime('next sunday');
$val["strtotime('next sun')"]    = strtotime('next sun');
$val["strtotime('last sunday')"] = strtotime('last sunday');
$val["strtotime('+4 sunday')"]   = strtotime('+4 sunday');
$val["strtotime('first sun of 2015-08')"] = strtotime('first sun of 2015-08');
$val["strtotime('last sun of 2015-08')"]  = strtotime('last sun of 2015-08');
$val["strtotime('last sun of 2039-08')"]  = strtotime('last sun of 2039-08');

//月
$val["strtotime('+2 month')"] = strtotime('+2 month');
$val["strtotime('-2 month')"] = strtotime('-2 month');
$val["strtotime('2015/08/01 +1 month')"] = strtotime('2015/08/01 +1 month');
$val["strtotime('+1 month', strtotime('2015/08/01'))"] = strtotime('+1 month', strtotime('2015/08/01'));
$val["strtotime('2015/08/31 +1 month')"] = strtotime('2015/08/31 +1 month');
$val["strtotime('2015-08 +1 month')"] = strtotime('2015-08 +1 month');
$val["strtotime('2039-08 +1 month')"] = strtotime('2039-08 +1 month');
$val["strtotime('first day of 2015/08/12')"] = strtotime('first day of 2015/08/12');
$val["strtotime('first day of', strtotime('2015/08/12'))"] = strtotime('first day of', strtotime('2015/08/12'));
$val["strtotime('last day of 2015/08/12')"] = strtotime('last day of 2015/08/12');
$val["strtotime('last day of next month')"] = strtotime('last day of next month');

//年
$val["strtotime('+1 year')"] = strtotime('+1 year');
$val["strtotime('-1 year')"] = strtotime('-1 year');
$val["strtotime('-1 year 13:23:33')"]   = strtotime('-1 year 13:23:33');
$val["strtotime('2015/01/01 -1 year')"] = strtotime('2015/01/01 -1 year');
$val["strtotime('-1 year', strtotime('2015/01/01'))"] = strtotime('-1 year', strtotime('2015/01/01'));

//時分秒
$val["strtotime('+2 hour')"] = strtotime('+2 hour');
$val["strtotime('+4 min')"]  = strtotime('+4 min');
$val["strtotime('+6 sec')"]  = strtotime('+6 sec');

echo "<table>\n";
foreach ($val as $k=>$v){
    echo "<tr><td>{$k}</td><td>". date('Y/m/d H:i:s', $v). "</td></tr>\n";
}
echo "<tr><td>strtotime('now') - strtotime('today')...今日の 0時からの秒数</td><td>". (strtotime('now') - strtotime('today')). "</td></tr>\n";
echo "<tr><td>strtotime('now') - strtotime('today 10:00:00')...今日の10時からの秒数</td><td>". (strtotime('now') - strtotime('today 10:00:00')). "</td></tr>\n";
echo '</table>';
?>
実行結果

3. mktime — 日付を Unix のタイムスタンプとして取得する

できることはstrtotime()と同じ。パラメターの指定方法が違う。

https://www.php.net/manual/ja/function.mktime.php

3-1. 書式

mktime(
    int $hour,
    ?int $minute = null,
    ?int $second = null,
    ?int $month = null,
    ?int $day = null,
    ?int $year = null
): int|false

//省略されたり、null が指定されたオプションの引数は、 ローカルの日付と時刻に従って、現在の値にセットされます。

3-2. パラメタ


$hour,
  month、day、yearの指定日付の0時から数えた「時」。
  負の値は、その日の 0時から前にさかのぼった時間を表す。
  23より大きい値は、その翌日以降の時間を表す。

$minute
  hour時の0分から数えた「分」。
  負の値は、その前の時刻を表す。
  59より大きい値は、その次の時間以降の時間を表す。
  省略時は現在の分。

$second
  minute分0秒から数えた「秒」。
  負の値は、その前の時刻を表す。
  59より大きい値は、その次の分以降の時間を表す。
  省略時は現在の秒。

$month
  前年末から数えた月数。
  1 〜 12の場合は、カレンダーどおりのその年の「月」を表す。
  1より小さい値は、前年の月を逆順でたどる。(0なら 12月、-1 なら 11月)
  12より大きい値は、その翌年以降の月を表す。
  省略時は現在の月。

$day
  前月末から数えた日数。
  1 〜 28、29、30、31(月によって異なる)までの場合は、その月の「日」を表す。
  1より小さい値は、前月の日を逆順でたどる。(0なら前月の末日、-1ならそのさらに前日)
  その月の日数より大きい値は、翌月以降の日を表す。
  省略時は現在の日です。

$year
  年。4桁の値で指定する。

3-3. 使用例

$ php -v
PHP 8.2.12 (cli) (built: Nov  5 2023 21:48:15) (NTS)
  :
$ php -a
Interactive shell

php > date_default_timezone_set('Asia/Tokyo');
php >
php > $dateStr = '2039/8/31';
php > list($yyyy, $mm, $dd) = explode('/', $dateStr);
php >
php > echo "{$dateStr} +1ヶ月 -> ". date('Y/m/d H:i:s', mktime(0, 0, 0, $mm + 1, $dd, $yyyy));
2039/8/31 +1ヶ月 -> 2039/10/01 00:00:00
php > echo "{$dateStr} +1ヶ月の初日 -> ". date('Y/m/d H:i:s', mktime(0, 0, 0, $mm + 1, 1, $yyyy));
2039/8/31 +1ヶ月の初日 -> 2039/09/01 00:00:00
php > echo "{$dateStr} +1ヶ月の末日 -> ". date('Y/m/d H:i:s', mktime(0, 0, 0, $mm + 2, 0, $yyyy));
2039/8/31 +1ヶ月の末日 -> 2039/09/30 00:00:00

4. あとがき

日付を扱う方法にはフレームワークが用意しているもの(CakePHPのTimeとか)を使う方法もありますが、私はdate関数とstrtotime関数を使った日付操作の方をよく使います。どんな環境に持って行っても動くってのは安心ですよね。あとあれだ、strtotimeの柔軟性。yyyy年mm月の最終日曜日(’last sun of yyyy-mm’)とか指定できるの便利。

お客さんに確認して日付ライブラリを指定されていなかったらこっちで作成します。

5. 追記(2023/10)2038年問題について

「php 2038年」で検索すると「date関数/strtotime関数は2038年以降は使えない」とヒットしますが、OSが64bitでPHPが64bit版なら2038年問題は発生しないです。

PHP_INT_SIZEが8なら大丈夫。4だとNG。(以下はCentOS7 & PHP5.6で確認)

$ cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
$ php -v
PHP 5.6.40 (cli) (built: Jun  7 2022 14:47:56)
  :
$ php -a
Interactive shell

php > echo PHP_INT_SIZE;
8
php > date_default_timezone_set('Asia/Tokyo');
php > $dateStr = '2039/8/31';
php > list($yyyy, $mm, $dd) = explode('/', $dateStr);
php > echo "{$dateStr} +1m-> ". date('Y/m/d H:i:s', mktime(0, 0, 0, $mm + 1, $dd, $yyyy));
2039/8/31 +1m-> 2039/10/01 00:00:00