こんにちは、天方です。
今日はとても技術よりな話になります。エンジニアでない方がご覧になる場合には、エンジニアってこんなこと考えて仕事しているんだと思っていただければと思います。
さて、最近は、Magento2のソースコードをみることが多くなってきました。 そんななかでMagento2のトリッキーなバグ?を見つけました。
特定のOSで日本語ローケールを使うと日付がおかしくなる?
テストサーバのMagento2で管理画面のロケールを日本語にすると一覧などで「2016yyy/1/31 12:13:15」と表示されていました。 yyyが余計ですね。

ロケールを英語に戻すと処理は正常に動いていました。 他の人に相談すると、自分の環境ではそんなことにはらないとの回答。 あれ?確かにテストサーバだとなるけど、自分のマシンだとならない・・・。特定のOSだけ?なんでなんだ? ということで調べてみました。
さっそく原因調査
原因を探るには、この日付が表示されるまでの仕組みを理解する必要があります。 仕組みはざっくりいうと、
- サーバ側で一覧に表示する日付データをDBから取得する。
- サーバ側で一覧画面で表示するときに日付に使うロケールを元に、デフォルトの日付フォーマットを取ってくる。
- サーバ側でデフォルトの日付フォーマットの形式で、一覧で表示するデータとそのフォーマット形式をjson形式にする。
- クライアント側で、json形式の日付データと、日付フォーマットを解釈して、一覧に日付を表示する。
というような流れです。1~3はPHP、4はJavaScriptで処理されています。
まず注目したのが3番の処理です。 上手くうごいている環境では jsonの中のデータとフォーマットを見ると
日付データ:2016/01/31
日付フォーマット:y/MM/dd
でしたが、上手く動かない環境だと
日付データ:2016/01/31
日付フォーマット:yyyy/MM/dd
となっていました。 yの数が違いますね。
調べたところphp-intlライブラリのIntlDateFormatter関数がこの2つを日付フォーマットとして許容していることがわかりました。
さらに深く調べるとどうやら大本はicuという国際化ライブラリのようです この国際化ライブラリでは「y」も「yyyy」も日付フォーマットで4桁の西暦という意味で使えるそうです。つまり同じ意味で二種類の表現があるんですね。 これはシステム屋にとってはあまりうれしくない状況です。(できれば同じ意味で2種類の表現をつかわないでほしい・・・)
これで原因はわかりました。後者のyyyyだと、最初の一文字が西暦と認識されてあとの3文字のyはそのまま表示されてしまうので「2016yyy/1/31」になるということです。 日本語の、しかも特定のOS環境でだけ再現するバグという日本人以外にはあまり遭遇しないバグだったんですね。 (その特定のOS環境でも、英語だと「y」を使っていました。英語以外の言語だと「yyyy」の場合もあれば「y」もあるという状況でした。)
では、どうすれば、この問題を解決できるのだろうか?
今回の場合、どうやってこのバグに対応するか非常に悩みました。大きくわけて方法は二つあります。
- 日本語ロケールでのデフォルトの日付フォーマットを返すライブラリで「yyyy」を使わない設定をする。
- yyyyが来ても、適切に処理するようにクライアント側の処理を直す。
です。 aはMagentoには手をいれず環境側の設定でなんとかしようというものです。bはMagentoのソースコードを修正または拡張することになります。
結論として、今回はbで対応することにしました。 「yyyy」を返しているphp-intlは仕様として正しいしそういう環境もあるようですから、Magento側で対応するのがよいと考えました。 (それに、もしaをするとなると、OSでメンテされているライブラリを自分で作り直す必要がありそうでした。とてもそこまでは手が回らなそうです。)
今後のこの不具合はどうなるのか?
対応方法はとりあえず弊社で開発しているモジュールで暫定対応しMagentoの本家にも修正パッチを送りました。 修正パッチはこちら
今後、Magento2本体に取り込まれることになると思います。 パッチを見ていただくとわかりますが、この修正、たった一行を追加しているだけです。 ただ、その一行を追加するまでの調査や思考には非常に長い時間がかかることが おわかりいただけたと思います。
これはほんの一部ですが、フラッツではこういった形で日々Magento2が日本でも使えるようにする活動をしています。
またなにかありましたら、こちらで紹介したいと思います。