フォーマットを変換するためにこのようなコードを書いていたのですが
$date='2017/11'; createFromFormat('Y/m',$date)->format('Ym');
一見正しそうなこのコード
これが’201712′になってしまうというおかしな挙動に見舞われました。
2018/01/31日に行うと11月は31日がないので12月1日の日付ができてしまうのだ。
つまり現在のシステム日付が29~31日の時だけに起こる。
これは実行する日によって正常であるのに特定の日だけにおこるという非常に困ったバグを引き起こすということです。
APIをよく読むと
http://php.net/manual/ja/datetime.createfromformat.php「format に文字 ! が含まれない場合は、作成した時刻値のうち format で指定されていない部分を 現在のシステム時刻で初期化します。」 とあります。
ソースまで読んではいないけど、”format で指定されていない部分”だから月が書き換わってしまうのはおかしいように思うが この初期化というのが後でおこなわれているとすればこのようになるのではないかと思われ これがバグではなく仕様だということになると上記のコードはまずいということになる。
よく見るとUser Contributed Notes 12にあるコードがそれを回避するコードが書かれています。
テストコード
<?php $dates = [ '2016/01', '2016/02', '2016/03', '2016/04', '2016/05', '2016/06', '2016/07', '2016/08', '2016/09', '2016/10', '2016/11', '2016/12', '2017/01', '2017/02', '2017/03', '2017/04', '2017/05', '2017/06', '2017/07', '2017/08', '2017/09', '2017/10', '2017/11', '2017/12', '2018/01', '2018/02', ]; function chk_date($date) { $datetime = DateTime::createFromFormat ( 'Y/m', $date ); echo var_export($datetime,true)."\n"; echo $datetime->format('Ym')."\n"; } $system = new DateTime(); echo "system->".var_export($system,true)."\n\n"; foreach ( $dates as $date ) { echo $date . "->\n"; chk_date( $date ); echo "<-\n"; }
2018/02/01 14:42に行った結果は想定通りの結果だけど
system->DateTime::__set_state(array( 'date' => '2018-02-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 2016/01-> DateTime::__set_state(array( 'date' => '2016-01-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201601 <- 2016/02-> DateTime::__set_state(array( 'date' => '2016-02-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201602 <- 2016/03-> DateTime::__set_state(array( 'date' => '2016-03-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201603 <- 2016/04-> DateTime::__set_state(array( 'date' => '2016-04-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201604 <- 2016/05-> DateTime::__set_state(array( 'date' => '2016-05-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201605 <- 2016/06-> DateTime::__set_state(array( 'date' => '2016-06-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201606 <- 2016/07-> DateTime::__set_state(array( 'date' => '2016-07-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201607 <- 2016/08-> DateTime::__set_state(array( 'date' => '2016-08-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201608 <- 2016/09-> DateTime::__set_state(array( 'date' => '2016-09-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201609 <- 2016/10-> DateTime::__set_state(array( 'date' => '2016-10-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201610 <- 2016/11-> DateTime::__set_state(array( 'date' => '2016-11-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201611 <- 2016/12-> DateTime::__set_state(array( 'date' => '2016-12-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201612 <- 2017/01-> DateTime::__set_state(array( 'date' => '2017-01-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201701 <- 2017/02-> DateTime::__set_state(array( 'date' => '2017-02-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201702 <- 2017/03-> DateTime::__set_state(array( 'date' => '2017-03-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201703 <- 2017/04-> DateTime::__set_state(array( 'date' => '2017-04-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201704 <- 2017/05-> DateTime::__set_state(array( 'date' => '2017-05-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201705 <- 2017/06-> DateTime::__set_state(array( 'date' => '2017-06-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201706 <- 2017/07-> DateTime::__set_state(array( 'date' => '2017-07-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201707 <- 2017/08-> DateTime::__set_state(array( 'date' => '2017-08-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201708 <- 2017/09-> DateTime::__set_state(array( 'date' => '2017-09-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201709 <- 2017/10-> DateTime::__set_state(array( 'date' => '2017-10-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201710 <- 2017/11-> DateTime::__set_state(array( 'date' => '2017-11-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201711 <- 2017/12-> DateTime::__set_state(array( 'date' => '2017-12-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201712 <- 2018/01-> DateTime::__set_state(array( 'date' => '2018-01-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201801 <- 2018/02-> DateTime::__set_state(array( 'date' => '2018-02-01 14:42:13.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201802 <-
システム日付を2018/01/31にしてみると
system->DateTime::__set_state(array( 'date' => '2018-01-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 2016/01-> DateTime::__set_state(array( 'date' => '2016-01-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201601 <- 2016/02-> DateTime::__set_state(array( 'date' => '2016-03-02 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201603 <- 2016/03-> DateTime::__set_state(array( 'date' => '2016-03-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201603 <- 2016/04-> DateTime::__set_state(array( 'date' => '2016-05-01 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201605 <- 2016/05-> DateTime::__set_state(array( 'date' => '2016-05-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201605 <- 2016/06-> DateTime::__set_state(array( 'date' => '2016-07-01 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201607 <- 2016/07-> DateTime::__set_state(array( 'date' => '2016-07-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201607 <- 2016/08-> DateTime::__set_state(array( 'date' => '2016-08-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201608 <- 2016/09-> DateTime::__set_state(array( 'date' => '2016-10-01 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201610 <- 2016/10-> DateTime::__set_state(array( 'date' => '2016-10-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201610 <- 2016/11-> DateTime::__set_state(array( 'date' => '2016-12-01 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201612 <- 2016/12-> DateTime::__set_state(array( 'date' => '2016-12-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201612 <- 2017/01-> DateTime::__set_state(array( 'date' => '2017-01-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201701 <- 2017/02-> DateTime::__set_state(array( 'date' => '2017-03-03 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201703 <- 2017/03-> DateTime::__set_state(array( 'date' => '2017-03-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201703 <- 2017/04-> DateTime::__set_state(array( 'date' => '2017-05-01 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201705 <- 2017/05-> DateTime::__set_state(array( 'date' => '2017-05-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201705 <- 2017/06-> DateTime::__set_state(array( 'date' => '2017-07-01 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201707 <- 2017/07-> DateTime::__set_state(array( 'date' => '2017-07-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201707 <- 2017/08-> DateTime::__set_state(array( 'date' => '2017-08-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201708 <- 2017/09-> DateTime::__set_state(array( 'date' => '2017-10-01 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201710 <- 2017/10-> DateTime::__set_state(array( 'date' => '2017-10-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201710 <- 2017/11-> DateTime::__set_state(array( 'date' => '2017-12-01 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201712 <- 2017/12-> DateTime::__set_state(array( 'date' => '2017-12-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201712 <- 2018/01-> DateTime::__set_state(array( 'date' => '2018-01-31 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201801 <- 2018/02-> DateTime::__set_state(array( 'date' => '2018-03-03 00:00:14.000000', 'timezone_type' => 3, 'timezone' => 'Asia/Tokyo', )) 201803 <-
なんてことでしょう。2/4/6/9/11のところが月が変わってしまってます
この記事へのコメント
トラックバックURL