2020年6月11日: 追記
更新依頼をかけてくださった親切な方のおかげで現在は令和対応しています。(コメント欄参照)
remirepo の libicu62 を 62.2 に更新してもらいました。
現在は普通にしていれば対応していると思います。
https://github.com/remicollet/remirepo/issues/149ただし、ICU 62のままなので、元年対応はありません。
(将来的には ICU 65 か何かに更新する予定と上の issue に書いてあります)
はじめに
和暦を含んだ文字列をタイムスタンプに変更する処理をPHPのextensionであるIntlを使って実現してました。
$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.2.19 — cli) by Justin Hileman
>>> $formatter = \IntlDateFormatter::create(
"ja_JP@calendar=japanese",
\IntlDateFormatter::FULL,
\IntlDateFormatter::FULL,
"Asia/Tokyo",
\IntlDateFormatter::TRADITIONAL,
'Gyy年MM月dd日'
);
=> IntlDateFormatter {#2665}
>>> $formatter->parse('平成31年6月1日')
=> 1559314800
>>> $formatter->parse('令和1年6月1日')
=> false
やっぱ令和はPHP7.2系の最新のバージョンでもだめだよねー。
環境
PHPはremi経由で入れています。
$ php -v
PHP 7.2.19 (cli) (built: May 29 2019 11:04:13) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
$ sudo yum list installed | grep php-intl
php72-php-intl.x86_64 7.2.19-2.el7.remi @remi-safe
調査
元号をタイムスタンプ変換しているところはどこか?PHPのソースコードを探索していきました。
static void internal_parse_to_timestamp(IntlDateFormatter_object *dfo, char* text_to_parse, size_t text_len, int32_t *parse_pos, zval *return_value)
{
...
timestamp = udat_parse( DATE_FORMAT_OBJECT(dfo), text_utf16, text_utf16_len, parse_pos, &INTL_DATA_ERROR_CODE(dfo));
...
}
udat_parse
はなんぞや?udat_parse
でググるとICU
というCのライブラリに。
http://www.icu-project.org/apiref/icu4c/udat_8h.html
令和対応は入っているか?ICU 令和
でググると先人の知恵が記事になってました。これよりICUのバージョンが64.2以上だと良さそうです。
【最終版】新元号に対応するために ICU を用いて和暦の動作確認をする
php-intlの依存性チェックをすると、バージョンは62のようです。なるほど。
$ yum deplist --disablerepo=base --enablerepo=remi-php72 php-intl
...
dependency: libicudata.so.62()(64bit)
provider: libicu62.x86_64 62.1-3.el7.remi
dependency: libicui18n.so.62()(64bit)
provider: libicu62.x86_64 62.1-3.el7.remi
dependency: libicuio.so.62()(64bit)
provider: libicu62.x86_64 62.1-3.el7.remi
dependency: libicuuc.so.62()(64bit)
provider: libicu62.x86_64 62.1-3.el7.remi
...
試行錯誤
libicu62.x86_64
をlibicu64.x86_64
に差し替えたphp-intlなら動くんじゃないかと考えて実行してみました。
下記のQiita記事を参考にしました。
さくらのレンタルサーバPHP7環境でintlモジュール有効化
php-intlをアンインストールし、作業用とビルドしたintl.soを置くフォルダを作成
$ yum remove libicu*
$ mkdir -p ~/usr/local/src
$ mkdir -p ~/usr/local/php/extension
ICUのビルド
$ cd ~/usr/local/src
$ wget http://download.icu-project.org/files/icu4c/64.2/icu4c-64_2-src.tgz
$ tar zxvf icu4c-64_2-src.tgz
$ cd icu/source
$ ./configure --prefix=$HOME/usr/local
$ gmake
$ gmake install
php-7.2.19に同梱されているintlをビルド
$ cd ~/usr/local/src
$ wget https://github.com/php/php-src/archive/php-7.2.19.zip
$ unzip php-7.2.19.zip
$ mv php-src-php-7.2.19/ext/intl ./intl-php-7.2.19
$ rm -rf php-src-php-7.2.19
$ cd intl-php-7.2.19
$ phpize
$ ./configure --with-icu-dir=$HOME/usr/local --with-php-config=/usr/bin/php-config
$ make
途中php_smart_str.h: No such file or directory
みたいなエラーが出る場合は、どうしたっけ…忘れました。多分、--with-php-config
の指定がまずかったのだと思います。
途中make: *** [intl_convertcpp.lo] Error 1
みたいなエラーが出る場合はここ参照してdevtoolset-7をインストールした上で下記コマンドを実行してください、
$ CXX=/opt/rh/devtoolset-7/root/usr/bin/g++ ./configure --with-icu-dir=$HOME/usr/local --with-php-config=/usr/bin/php-config
$ make
modulesフォルダにできたintl.soをさっき作成したフォルダへコピー
$ cp ~/usr/local/src/intl-php-7.2.19/modules/intl.so ~/usr/local/php/extension/
intlの設定ファイルを作成します。
$ sudo vi /etc/php.d/20-intl.ini
さっきビルドしたintl.soのパスを記述します。
extension=/home/dev/usr/local/php/extension/intl.so
これで動くはず。確認します。
$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.2.19 — cli) by Justin Hileman
>>> $formatter = \IntlDateFormatter::create(
"ja_JP@calendar=japanese",
\IntlDateFormatter::FULL,
\IntlDateFormatter::FULL,
"Asia/Tokyo",
\IntlDateFormatter::TRADITIONAL,
'Gyy年MM月dd日'
);
=> IntlDateFormatter {#2659}
>>> $formatter->parse('平成31年6月1日')
=> 1559314800
>>> $formatter->parse('令和1年6月1日')
=> 1559314800
うぇーい!
考察
とりあえずできました。
でもphp-intlのビルドでmake test
するとテスト失敗が結構でます。。。
=====================================================================
FAILED TEST SUMMARY
---------------------------------------------------------------------
IntlBreakIterator::getLocale(): basic test [tests/breakiter_getLocale_basic2.phpt]
Bug #66289 Locale::lookup incorrectly returns en or en_US if locale is empty [tests/locale_bug66289.phpt]
locale_get_display_language() [tests/locale_get_display_language.phpt]
locale_get_display_name() icu >= 53.1 [tests/locale_get_display_name5.phpt]
locale_get_primary_language() [tests/locale_get_primary_language.phpt]
locale_parse_locale() icu >= 4.8 [tests/locale_parse_locale2.phpt]
=====================================================================
一応動くとはいえ無理やりですし、結構手順もめんどくさいのでInfrastructure as a codeで環境作ってる場合はどうすんのコレ…って感じではあります。本番に入れるのはどうしようと迷う感じではあります。どうしよう。はやくphp-intl側に対応してもらいたいところです。いつになるんだろ。
参考リンク
- https://github.com/php/php-src/tree/master/ext/intl
- http://www.icu-project.org/apiref/icu4c/udat_8h.html
- https://qiita.com/midheartty/items/590b0911c000a24abe34
- https://qiita.com/masa-kunikata/items/bc34b33fdc169b660c58
- https://serverfault.com/questions/914288/how-to-build-standalone-intl-extension-from-bundled-source
- https://www.softwarecollections.org/en/scls/rhscl/devtoolset-7/