86
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

放課後アトリエといろ 第65535話が公開された

Last updated at Posted at 2019-11-20

放課後アトリエといろ

時の為政者によって封印されてしまった幻の名作、放課後アトリエといろ第65535話が特別公開されました
さあさっそく読むんだ。
そして本屋で単行本の1巻から4巻までを買い占めて編集部に再開のプレッシャーを掛けるんだ。

あ、言いたいことはもう終わったので帰っていいですよ。

でもここで終わると不思議なことに何故か消されてしまうので、ここから下にはなんか適当におまけでも置いときます。

Bug #78827 Wrong timestamp

さて問題です。
以下の出力はどうなるでしょう。

$today = strtotime('last Wednesday');

var_dump(
    (strtotime('last Wednesday') - $today) / 86400,
    (strtotime('last Wednesday + 1 day') - $today) / 86400,
    (strtotime('last Wednesday + 2 day') - $today) / 86400,
    (strtotime('last Wednesday + 3 day') - $today) / 86400,
    (strtotime('last Wednesday + 4 day') - $today) / 86400,
    (strtotime('last Wednesday + 5 day') - $today) / 86400,
    (strtotime('last Wednesday + 6 day') - $today) / 86400,
    (strtotime('last Wednesday + 7 day') - $today) / 86400,
    (strtotime('last Wednesday + 8 day') - $today) / 86400,
    (strtotime('last Wednesday + 9 day') - $today) / 86400,
);

Expected result

まず$todayは"先週の水曜日、0時0分0秒"になります。
実行したのが木曜であれば昨日、金曜であれば一昨日、そして今日が水曜であれば7日前になります。

ではvar_dumpの行を見ていきましょう。

まずlast Wednesdayの行ですが、ここは同じ値を引いてるだけなので当然0になります。

次にlast Wednesday + 1 dayですが、ここは"先週の水曜日から+1日、0時0分0秒"となります。
実行したのが木曜であれば今日、金曜であれば昨日のことで、そして今日が水曜であれば6日前になります。
単に1日ずらしただけなので、当然ながら$todayとのずれは必ず1日です。
ということで(strtotime('last Wednesday + 1 day') - $today) / 86400の答えは、何曜日に実行したとしても必ず1です。

同じくlast Wednesday + 2 dayの答えは必ず2、last Wednesday + 3 dayの答えは必ず3、となります。
そんなかんじで続いていって、一番下のlast Wednesday + 9 dayの答えは必ず9です。
20191120-01.png
当然ですね。

Actual result

水曜日に実行するとこうなります。
20191120-02.png
なんでだよ。

・水曜日以外にstrtotime('last Wednesday + 6 day')を計算すると、6日後の値になる。
・水曜日以外にstrtotime('last Wednesday + 7 day')を計算すると、7日後の値になる。
・水曜日にstrtotime('last Wednesday + 6 day')を計算すると、6日後の値になる。
・水曜日にstrtotime('last Wednesday + 7 day')を計算すると、14日後の値になる。

書式の曜日が実行した曜日と同じ場合のみ、7日以上の日を足すと何故か値がずれるというバグです。

あまり使わないような書き方をしたうえで、さらに特定の曜日にしか発生しないということで、万一これを踏んでしまったら原因の特定が非常に困難になりそうなバグですね。

あまり使わないというのはつまり、普通はlast Wednesday + 7 dayじゃなくてnext Wednesdayって書くでしょうということですが。

next Wednesday

このバグチケには載ってないのですが、実はnextでも同じバグが起こります。

$today = strtotime('next Wednesday');

var_dump(
    (strtotime('next Wednesday') - $today) / 86400,
    (strtotime('next Wednesday - 1 day') - $today) / 86400,
    (strtotime('next Wednesday - 2 day') - $today) / 86400,
    (strtotime('next Wednesday - 3 day') - $today) / 86400,
    (strtotime('next Wednesday - 4 day') - $today) / 86400,
    (strtotime('next Wednesday - 5 day') - $today) / 86400,
    (strtotime('next Wednesday - 6 day') - $today) / 86400,
    (strtotime('next Wednesday - 7 day') - $today) / 86400,
    (strtotime('next Wednesday - 8 day') - $today) / 86400,
    (strtotime('next Wednesday - 9 day') - $today) / 86400,
);

水曜日以外に実行すると[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]ですが、水曜日に実行すると[0, -8, -9, -10, -11, -12, -13, -14, -15, -16]になります。
こちらは-1で起こるので、より深刻ですね。

Bug #78496 date_create rolls a day back on year 0000 timestamp

問題です。
以下の出力はどうなるでしょう。

$timestamp = strtotime('0000-02-20 00:00:00 UTC');
echo gmdate('Y-m-d H:i:s', $timestamp);
echo date_create('@' . $timestamp)->format('Y-m-d H:i:s');

Expected result

当然ですが、どちらも"0000-02-20 00:00:00"になりますよね。

Actual result

20191120-03.png

なんでだよ。

0年についてはdate_createが正しく計算されないというバグがあります。
西暦0年は存在しないとかいうツッコミが聞こえてきそうですが、ISO 8601では"0000"年は紀元前1年、"-0001"年は紀元前2年を表します。
めっちゃわかりにくい。

date_create

date_create関数のヘルプには『DateTime::__construct() のエイリアス』と書かれているのですが、これ嘘です。

var_dump(date_create('2020/01/32')); // false
var_dump(new DateTime('2020/01/32')); // Fatal error: Uncaught Exception: DateTime::__construct(): Failed to parse time string

書式が理解できない場合、date_createはfalseを返しますがDateTime::__construct()は例外を起こします。

Bug #78452 diff makes wrong in hour for Asia/Tehran

さて問題です。
以下の出力はどうなるでしょう。

date_default_timezone_set('Asia/Tokyo');
$date1 = new \DateTime('2019-11-05 11:00:00');
$date2 = new \DateTime('2019-11-01 12:00:00');
echo $date1->diff($date2)->format('%H');

date_default_timezone_set('America/New_York');
$date1 = new \DateTime('2019-11-05 11:00:00');
$date2 = new \DateTime('2019-11-01 12:00:00');
echo $date1->diff($date2)->format('%H');

Expected result

今回出力しているのは時間の差分なので、月が違うことは影響しません。
東京は当然ですがそのまま23ですね。
ニューヨークは夏時間切り替えを挟むのでどういう計算になるのかよくわからないのですが、時間差を求める場合は23でいいのですかね?

Actual result

78452.png
なんでだよ。

夏時間の切り替えをまたぐと何故かマイナスになることがあります。
正解がどの値かはわかりませんが、少なくとも-1は明らかに間違いですね。

夏時間を考えてない勘違いバグ報告はわりとよくあるのですが、このチケットについては間違いなくバグです。

Bug #78504 Add warnings to DateTime object with incompatible timezone info

さて問題です。
以下の出力はどうなるでしょう。

$tz = new DateTimeZone('Asia/Tokyo');
$ts = strtotime('2020-01-1 00:00:00');

$dt1 = DateTime::CreateFromFormat("YmdHis", date('YmdHis', $ts), $tz);
$dt2 = DateTime::CreateFromFormat("U", date('U', $ts), $tz);

var_dump(
    $dt1->format("Y-m-d H:i:s"),
    $dt2->format("Y-m-d H:i:s"),
);

Expected result

当然、両方とも2020-01-01 00:00:00ですね。

Actual result

前者は2020-01-01 00:00:00、後者は2019-12-31 15:00:00です。
なんでだよ。

実はパラメータをタイムスタンプで渡した場合、引数のタイムゾーンは無視されてUTC扱いされます。

これはマニュアルに書かれているのですが、あまりにわかりにくいですね。
従って、この挙動はPHP8か9あたりで変更になる可能性があります。

and

さて問題です。
以下の出力はどうなるでしょう。

$diff = DateInterval::createFromDateString('1 year and 2 months');

Expected result

1年2ヶ月差のDateIntervalオブジェクト。

Actual result

PHP7.2.17以降、そして7.3.4以降エラーになります

ChangeLogには全く書かれていません。
おそらく50020のバグ修正と一緒に入り込んだものではないかと思いますが確証はありません。

何が問題かって単に'and'という文字列に対応してないということなので、'1 year 2 months'にすれば問題なく動作します
元々対応してなかったのにたまたま動いていたというやつなので、使う方が悪いといえばそうなのですが、でもサイレントに挙動が変わるとびくっとなりますね。

まとめ

公開されたとか言ってたけど、続きが読みたくてどうしようもなくて我慢できなかったから描いてもらったんですけどね。
さあみんなもどんどん依頼するのだ。

86
41
4

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
86
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?