[PHP] PHPでの日付文字列パースと要素取り出し

2018年5月14日月曜日

PHP

PHPで日付文字列をパースして、そこから曜日を算出する必要があった。
こんなの簡単だろと思ってたんだけど、いくつか気をつける点があったので、メモ。

strptime

まずはstrptimeを使って、こんな感じにした。
$d = (strptime("20180507", "%Y%m%d"));
var_dump($d);
array(9) {
  ["tm_sec"]=>
  int(0)
  ["tm_min"]=>
  int(0)
  ["tm_hour"]=>
  int(0)
  ["tm_mday"]=>
  int(7)
  ["tm_mon"]=>
  int(4)
  ["tm_year"]=>
  int(118)
  ["tm_wday"]=>
  int(1)
  ["tm_yday"]=>
  int(126)
  ["unparsed"]=>
  string(0) ""
}

20180507は月曜日なので、$d["tm_wday"] には 1 が入っている。

でも、strptimeはWindowsでは使えないらしい。環境が変わった場合を考えると、別の手段を取った方が良さそう。

http://php.net/manual/ja/function.strptime.php

DateTime

DateTimeオブジェクトを使うとこうなる。
$d = DateTime::createFromFormat("Ymd", "20180507");
var_dump($d);
object(DateTime)#7 (3) {
  ["date"]=>
  string(26) "2018-05-07 05:17:55.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(3) "UTC"
}

日付だけでは、時刻に現在時刻が自動的に格納されてしまうようだ。
デフォルトのタイムゾーンが何になっているかは環境によるけど、時刻を00:00:00に合わせたい場合は時刻も指定する必要がある。
$d = DateTime::createFromFormat("YmdHis", "20180507" . "000000");
var_dump($d);
object(DateTime)#7 (3) {
  ["date"]=>
  string(26) "2018-05-07 00:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(3) "UTC"
}
※今回の検証環境ではデフォルトタイムゾーンがUTCになっている。

http://php.net/manual/ja/class.datetime.php

さて、これでDateTimeオブジェクトが出来上がったけど、ここから直接曜日は取り出せないので、一度、タイムスタンプに変換して、getdateを使う。
var_dump(getdate($d->getTimestamp()));
array(11) {
  ["seconds"]=>
  int(0)
  ["minutes"]=>
  int(0)
  ["hours"]=>
  int(0)
  ["mday"]=>
  int(7)
  ["wday"]=>
  int(1)
  ["mon"]=>
  int(5)
  ["year"]=>
  int(2018)
  ["yday"]=>
  int(126)
  ["weekday"]=>
  string(6) "Monday"
  ["month"]=>
  string(3) "May"
  [0]=>
  int(1525651200)
}

これで、["wday"]を参照すれば、曜日のインデックスが取得できる。

http://php.net/manual/ja/function.getdate.php

DateTimeImmutable

DateTimeの挙動でもう一つ気をつけたいのはadd等での日付計算。
addメソッドは指定したDateInterval分の間隔を足したDateTimeを返してくるけど、自分自身の値も変えてしまう。
$d = DateTime::createFromFormat("YmdHis", "20180507" . "000000");
print($d->format("Ymd") . "\n");
$d2 = $d->add(new DateInterval("P1M"));
print($d2->format("Ymd") . "\n");
print($d->format("Ymd") . "\n");
20180507
20180607
20180607

$d2だけでなく、元の$dも6月になってしまっている。

これが嫌な場合は、DateTimeImmutableを使おう。
$d = DateTimeImmutable::createFromFormat("YmdHis", "20180507" . "000000");
print($d->format("Ymd") . "\n");
$d2 = $d->add(new DateInterval("P1M"));
print($d2->format("Ymd") . "\n");
print($d->format("Ymd") . "\n");
20180507
20180607
20180507

http://php.net/manual/ja/class.datetimeimmutable.php