"BOKU"のITな日常

BOKUが勉強したり、考えたことを頭の整理を兼ねてまとめてます。

PHPの日付時刻ライブラリCarbon/超便利だけど和暦表示だけ注意したほうがいい理由

f:id:arakan_no_boku:20190922143447p:plain

目次

PHPの日付時刻ライブラリCarbon

CarbonならDateTimeの全てを継承し、地域化のサポートやDateTimeオブジェクトの加減算やフォーマット方法が追加されている非常に便利な日付時刻ライブラリです。

Laravelなどのフレームワークにも同梱されています。

非常に多機能なので、すべてを紹介なんかできませんが、とりあえず使う程度のさわりと、注意点を整理したいと思います。

Carbonのインストール

CarbonはLaravelをインストールしたら、いっしょにはいります。

Laravelを使わないならプロジェクトにインストールする必要があります。

プロジェクトフォルダをカレントにして、composerでインストールします。

composer require nesbot/carbon

カレントフォルダのvenderの下にインストールされます。

Carbonの簡単なサンプル

Carbonを使うには、「use Carbon\Carbon」します。

下記サンプルでは、オートローダ「require __DIR__ . "/../vendor/autoload.php";」を使ってます。

まずは、Carbonを使ったサンプルプログラムソースです。

src\CarbonSample.php

<?php
require __DIR__ . "/../vendor/autoload.php";

use Carbon\Carbon;

// 開始日付オブジェクトの生成
$raw = '22. 11. 1968';
$start = Carbon::createFromFormat('d. m. Y', $raw);
echo '開始日: ' . $start->format('Y年m月d日') . "\n";

echo "--------------------------------------------\n";

// 開始日付をコピーして、1か月と6日を足して、終了日にする
$end = clone $start;
$end->add(new DateInterval('P1M6D'));

// 開始日と終了日の差を計算して何カ月・何日差があるかを表示する
$diff = $end->diff($start);
echo '開始日と終了日の差: ' . $diff->format('%m ケ月, %d 日 (合計: %a 日の差がある)') . "\n";

echo "--------------------------------------------\n";

// 日付同士を比較もできる
if ($start < $end) {
    echo "開始日は終了日よりも前の日付です。!\n";
}

echo "--------------------------------------------\n";

// 期間オブジェクトを作って、期間中の木曜日の日付をリストする
$periodInterval = DateInterval::createFromDateString('first thursday');
$periodIterator = new DatePeriod($start, $periodInterval, $end, DatePeriod::EXCLUDE_START_DATE);
echo "開始日と終了日の間の期間にある木曜日をリストします。\n";
foreach ($periodIterator as $date) {
    // 毎木曜日を表示する
    echo $date->format('Y年m月d日') . "\n";
    
}

CarbonはDateTimeを全継承しています。

https://secure.php.net/book.datetime

なので、DateTimeと非常に親和性が高いです。

おおむね、DateTimeで書かれている元ソースがあったとしたら、数か所書き直すだけでCarbonに移行できます。

上記サンプルも、元はDateTimeで書かれていたのを以下の1か所書き直しただけです。

元が

$start = DateTime::createFromFormat('d. m. Y', $raw);

 となっていたところを、

 $start = Carbon::createFromFormat('d. m. Y', $raw);

 と書き換えただけです。

上記を、コマンドラインで「php .\src\CarbonSample.php」で動かすしました。

実行した結果はこんな感じになります。

f:id:arakan_no_boku:20190926012100p:plain 

 

Carbonのそのほかの使用サンプル(リンク)

Carbonは多機能です。

でも、インタフェースがすっきりしているので、サンプルを見れば、なんとなくでも使えてしまう敷居の低さがありますが、この記事で紹介するまでもなく優れたサイトがあるので、リンクだけはっておきます。

まずは本家の方

carbon.nesbot.com

英語なので、自動翻訳してみれば良いと思います。

日本語だと、こちらですかね

blog.capilano-fw.com

実例 217件!

お疲れ様って感じです。

 

Carbonの注意点:和暦表示がWindowsではうまくいかない

和暦表示などのロケール情報を使った一部フォーマットで問題があります。

ネットで情報を見てると、和暦表示にCarbonの機能を使った例も紹介されていますし、うまくいくケースもあるのですが、「Windows」ではうまくいきません。

例えば、以下のような曜日の日本語表示はうまくいきません。

曜日を(月)とか(木)のように日本曜日名で表示させるつもりでしたがダメでした。

setlocale(LC_ALL, 'ja_JP.UTF-8');
echo Carbon::now()->formatLocalized('%Y %B %d(%a)');

 それと、以下のような年の和暦表示もうまくいきません。

年を、平成31年とか令和1年とかの和暦で表示しようとしたのですがダメでした。

setlocale(LC_ALL, 'ja_JP.UTF-8');
Carbon::now()->formatLocalized('%EC%Ey年');

の2つです。

原因を調べてみると。

どうも「formatLocalized」というメソッドが、「かなりOSに依存する部分が大きいメソッド」らしいということ。

依存する部分を具体的に書くと

  • 「setlocale」のOS依存
  • 「format書式サポート」のOS依存

の2つです。

 

和暦表示がうまくいかない原因1:「setlocale」のOS依存

例えば・・。

上記のローカライズの前提になっている「setlocale(LC_ALL, 'ja_JP.UTF-8');」が、LinuxではOKですが、Windowsでは動かないらしいというものです。

理由は、こちらの記事に書いてありました。

docs.microsoft.com

たしかに

If you provide a code page value of UTF-7 or UTF-8, setlocale will fail, returning NULL.

UTF-7またはUTF-8のコードページ値を指定すると、setlocaleは失敗し、NULLを返します。)

とあります。

WindowsでもPHP5までは動いてた・・という情報もありますが、少なくとも、自分の「PHP7.3」+Windows環境では見事に動かないから、そうなんでしょう・・。

 

和暦表示がうまくいかない原因2:「format書式サポート」のOS依存

 和暦を表示する「%EC」とか「%E」という書式は、対応しているシステムとそうでないシステムがあるらしいのです。

これは自分も知らなくて、以下の記事で知りました。

teratail.com

結局。

Linuxでもディストリビューションによって差があり、Macはダメ。

自分が試した感じだと、Windowsもうまくいかないっぽい。

これは厳しいです。

 

結論:和暦表示だけはCarbonを使わない

Carbonは使うけど、和暦表示や曜日の日本語表示などについては、Carbonに頼らず、必要になったら別途用意する・・と考えた方が良さげです。

開発がWindowsで、本番がLinuxというケースは実際多いです。

OS依存のあるメソッドは使わないほうが賢明です。

幸いにして。

DateTimeを使って、和暦表示したり、曜日を日本語表示したりするようなTipsやサンプルはネットにいっぱいあります。

そこまで凝らずに、せいぜい明治以降の年だけ見ればよいなら、一から作っても、和暦表示自体それほど大変な手間がかかる・・ってほどでもありません。

それに。

今時、システムは西暦で統一というところも多く、和暦そのものが必要ない場合も増えてます。

官公庁に提出する書類を作るみたいな和暦が必要な要件ができてから、考えればいいんじゃないか・・と、個人的には気楽に考えています。

今回はこんなところで。

ではでは。