【第1回】PHPで日付や時刻を扱うときに便利なライブラリ「Carbon」

【第1回】Composerを使ってお手軽アプリケーション開発

本連載ではComposerで公開されているパッケージの中から、フレームワークを問わず汎用的に使えるライブラリをサンプルコードと共に紹介します。

第1回は日付や時刻を扱うときに便利なライブラリ「Carbon」の紹介です。

初めてComposerでライブラリを使い始めようとする人には、まずCarbonをおすすめしたいです。なぜならば、Carbonのライブラリは主要なクラスがひとつだけなので、使用している時にどのクラスのインスタンスを扱っているのか分からなくなるリスクが低いからです。

Carbonの概要

php-composer-carbon

Carbon(カーボン)はPHPに標準実装されているDateTimeクラスを継承したクラスで、Laravelなどのメジャーフレームワークでも採用されている日時を扱うクラスです。CarbonはDateTimeクラスの不便な部分を拡張しているので、時間の比較、時間の加算/減算をより便利に使うことができます。

公式サイト:https://carbon.nesbot.com/
GitHub:https://github.com/briannesbitt/carbon

Carbonの導入方法

Composerのインストールコマンドは下記です。

composer require nesbot/carbon

または、composer.jsonに下記を記載して、composer updateを実行します。

{
    "require": {
        "nesbot/carbon": "^1.33"
    }
}

※2018/08/24時点の最新バージョンは1.33.0です。

基本的なCarbonの使い方

Carbonインスタンスを生成する

Carbonでは日時をインスタンス化する際に、様々な入力フォーマットからインスタンス化を行うことができます。下記のサンプルコードでは例として、まず「現在の時刻のインスタンスを生成」、「日時形式の文字列からインスタンスを作成」、「UNIXTIMEからインスタンスを作成」を行います。

<?php

require_once __DIR__ . '/vendor/autoload.php';
use Carbon\Carbon;

// 現在の時刻からインスタンスを生成
$carbon = Carbon::now();
echo $carbon; // 2018-08-24 13:07:27

// 日時形式の文字列からインスタンスを作成
$carbon = Carbon::parse('2018/01/02 03:04:05');
echo $carbon; // 2018-01-02 03:04:05

// UNIXTIMEからインスタンスを作成
$carbon = Carbon::createFromTimestamp(1234567890);
echo $carbon; // 2009-02-13 23:31:30

さらに、Carbonでは今日の0時や昨日/明日、月初/月末のような集計処理でよく使いそうな日時も簡単に生成することができます。

<?php

require_once __DIR__ . '/vendor/autoload.php';
use Carbon\Carbon;

// 今日の0時0分0秒のインスタンスを作成
$today = Carbon::today();
echo $today . PHP_EOL; // 2018-08-24 00:00:00

// 昨日/明日のインスタンスを作成
$yesterday = Carbon::yesterday();
echo $yesterday . PHP_EOL; // 2018-08-23 00:00:00
$tomorrow = Carbon::tomorrow();
echo $tomorrow . PHP_EOL; // 2018-08-25 00:00:00

// 月初/月末のインスタンスを作成
$firstOfMonth = Carbon::now()->firstOfMonth();
echo $firstOfMonth . PHP_EOL; // 2018-08-01 00:00:00
$endOfMonth = Carbon::now()->endOfMonth();
echo $endOfMonth . PHP_EOL; // 2018-08-31 23:59:59

Carbonインスタンスから出力する

次はCarbonインスタンスから日時形式の文字列やUNIXTIMEを出力する方法です。当然ですが、CarbonはDateTimeクラスを継承しているので、formatgetTimestampといったメソッドを使用して出力できます。

<?php

require_once __DIR__ . '/vendor/autoload.php';
use Carbon\Carbon;

$carbon = Carbon::parse('2018/01/02 03:04:05');
// formatメソッド (指定した書式の日時フォーマットを返す)
echo $carbon->format('Y-m-d H:i:s') . PHP_EOL; // 2018-01-02 03:04:05

// getTimestampメソッド (int型のUNIXTIMEを返す)
var_dump($carbon->getTimestamp()); // int(1514862245)

インスタンス生成のサンプルでも使用していましたが、Carbonクラスには__toStringメソッドが実装されており、インスタンスをechoするだけで指定した日付フォーマットを返すことが可能です。デフォルトではY-m-d H:i:sのフォーマットで出力されますが、setToStringFormatメソッドで出力フォーマットの変更を行うことができます。

<?php

require_once __DIR__ . '/vendor/autoload.php';
use Carbon\Carbon;

$carbon = Carbon::parse('2018/01/02 03:04:05');
echo $carbon . PHP_EOL; // 2018-01-02 03:04:05

// デフォルトの表示を変更する
Carbon::setToStringFormat('Y/n/j G:i:s');
echo $carbon . PHP_EOL; // 2018/1/2 3:04:05

// デフォルトの表示を初期状態に戻す
Carbon::resetToStringFormat();
echo $carbon . PHP_EOL; // 2018-01-02 03:04:05

Carbonインスタンスを用いた時間の加算・減算

DateTimeクラスでは時間の加算や減算を行う際は、DateIntervalクラスを使用する必要があり面倒な処理が必要でしたが、Carbonでは簡単に時間の加算や減算を行うことができます。時間の各単位ごとに加算メソッド(add○○○)と減算メソッド(sub○○○)があるため、より直感的に時間の加算・減算を行うことができます。

<?php

require_once __DIR__ . '/vendor/autoload.php';
use Carbon\Carbon;

/* 日単位の加算・減算 */
$carbon = Carbon::parse('2018-01-01 00:00:00');
echo $carbon . PHP_EOL; // 2018-01-01 00:00:00
// インスタンスに1日加算する
$carbon->addDays(1);
echo $carbon . PHP_EOL; // 2018-01-02 00:00:00
// インスタンスに2日減算する
$carbon->subDays(2);
echo $carbon . PHP_EOL; // 2017-12-31 00:00:00

/* 時間単位の加算・減算 */
$carbon = Carbon::parse('2018-01-01 00:00:00');
echo $carbon . PHP_EOL; // 2018-01-01 00:00:00
// インスタンスに1日(=24時間)加算する
$carbon->addHours(24);
echo $carbon . PHP_EOL; // 2018-01-02 00:00:00
// インスタンスに2日(=48時間)減算する
$carbon->subHours(48);
echo $carbon . PHP_EOL; // 2017-12-31 00:00:00

サンプルでは紹介していませんが、上記と同様に分単位の加算・減算を行うメソッド(addMinutes/subMinutes)や、秒単位の加算・減算を行うメソッド(addSeconds/subSeconds)も、もちろん用意されています。

Carbonを使いこなすためのTips

ここまで、Carbonの基本的な使い方を紹介してきましたが、ここからはより実践的に使う内容を紹介していきます。

特定の日時を現在からの相対時間で表示する

相対時間というのは、○分前や○日後など現在の時間からどれくらい離れているかを表示するものです。自力で実装しようとすると分岐が多く面倒な処理ですが、Carbonには相対時間を簡単に表示するdiffForHumansメソッドが用意されています。

デフォルトで使用すると英語表記になっていますが、ロケールを指定するsetLocaleメソッドを使用することで、日本語表記の相対時間にすることができます。

$carbon = Carbon::parse('2020-07-24 20:00:00');

$now = Carbon::parse('2018-08-24 00:00:00');
echo $carbon->diffForHumans($now) . PHP_EOL; // 1 year after

// 相対時間の表示を日本語に変更
Carbon::setLocale('ja');

$now = Carbon::parse('2018-08-24 00:00:00');
echo "東京オリンピック開会式は{$carbon->diffForHumans($now)}" . PHP_EOL; // 東京オリンピック開会式は1年後

$now = Carbon::parse('2020-06-01 00:00:00');
echo "東京オリンピック開会式は{$carbon->diffForHumans($now)}" . PHP_EOL; // 東京オリンピック開会式は1ヶ月後

$now = Carbon::parse('2020-07-01 00:00:00');
echo "東京オリンピック開会式は{$carbon->diffForHumans($now)}" . PHP_EOL; // 東京オリンピック開会式は3週間後

$now = Carbon::parse('2020-07-20 00:00:00');
echo "東京オリンピック開会式は{$carbon->diffForHumans($now)}" . PHP_EOL; // 東京オリンピック開会式は4日後

CarbonでdiffForHumansメソッドを使う最大の利点は、記事の公開日時を○日前のように表示するときです。画面での出力は相対時間にしつつ、タグの属性では検索エンジンに正確な時間を伝えることができます。

$published_at = Carbon::parse('2018-04-01 00:00:00');
echo "<time datetime='{$published_at->toIso8601String()}'>{$published_at->diffForHumans()}</time>" . PHP_EOL; // <time datetime='2018-04-01T00:00:00+00:00'>4ヶ月前</time>

現在時刻を指定の時間にする

アプリケーション内でDateTimeクラスやtime()のような関数を使っているとテスト時に再現性がなくなってしまうため、ユニットテストなどで現在時刻を使用したテストケースは難しい場合がありました。

しかし、アプリケーション内で日付を操作する際にCarbonを使用していると、実行タイミングに関わらず容易に再現性を保つことができます。CarbonのsetTestNowメソッドでは、宣言以降に作成された現在時刻のインスタンスを指定した時刻のインスタンスとして、作成することができます。

echo Carbon::now() . PHP_EOL; // 2018-08-24 13:15:38

// 現在時刻を2018年1月1日0時0分0秒に指定
Carbon::setTestNow(Carbon::parse('2018-01-01 00:00:00'));
echo Carbon::now() . PHP_EOL; // 2018-01-01 00:00:00

// 現在時刻を戻す
Carbon::setTestNow(null);
echo Carbon::now() . PHP_EOL; // 2018-08-24 13:15:38

今月の日付を1日から末日までループで回す

1日から末日まで1日ごとの日付に対して処理を行う場合がバッチ処理などであると思います。以下では、現在の日付を含む月(=今月)の1日から末日まで1日ごとの日付に対して、処理を行うようなサンプルを作成しました。

$firstOfMonth = Carbon::now()->firstOfMonth();
$endOfMonth = $firstOfMonth->copy()->endOfMonth();

for ($i = 0; true; $i++) {
    $date = $firstOfMonth->addDays($i);
    if ($date > $endOfMonth) {
        break;
    }
    echo $date->format('Y-m-d') . PHP_EOL; //2018-08-01, 2018-08-02, ・・・, 2018-08-30, 2018-8-31
}

気をつけなければいけない点としては、Carbonのインスタンスはミュータブルなオブジェクトであることです。ミュータブル(mutable)とは、作成したオブジェクトの状態が変化する(時刻が変動する)オブジェクトのことを指します。逆に、イミュータブル(immutable)は一度作成した後は状態が変化しない(時刻が変わらない)オブジェクトのことを指します。

加算・減算のサンプルを改めて見ると、時間を加算したインスタンスが持つ時刻が変化していることが分かると思います。そのため、ループ内で$date = $firstOfMonth->addDays($i);のような処理を書いてしまうと、日付が1日ずつ進まず、"2018-08-01, 2018-08-02, 2018-08-04, 2018-08-07, 2018-08-11, ・・・"のようにループが進んでいきます。

上記のサンプルでは、copyメソッドを使用して、ループ内で1日のインスタンスを複製してから日付を加算することによって、1日ずつ進む処理を実現しています。

まとめ

今回は日付や時刻を扱うパッケージ「Carbon」の基本的な使い方と使いこなすためのTipsを紹介しました。Carbonを使うことで、日時をint型のUNIXTIMEやDateTimeクラスで扱うより、容易かつ便利に日時を操作することができました。

次回以降では、1つのパッケージで複数のクラスを扱うことになるため、まずCarbonでパッケージのクラスを扱うことに慣れましょう。