ミュータブル一覧

PHPの日付・時刻ライブラリChronosの機能について

以前、Laravelでも使用されている日付時刻を扱うライブラリーCarbonについて紹介しました。
PHPで日付時刻を扱う際には、date関数DateTimeクラスを使用して日付を求めたりすることが多いと思いますが、Chronosを使用し、日付時刻を扱うという方法もあります。

PHPで日付関連の処理をするのに便利なCarbonの使用方法について紹介しています。CarbonはGitHubでも多数のスターの付く人気の日付関連のライブラリーです。

Chronosは、CakePHPでもCakePHP3.2から日付時刻を扱うライブラリとして導入されています。

Chronosの利用環境

・PHP 5.5.0以上(DateTimeImmutableを継承しています)

CarbonとChronosの違い

Carbon(1系)の場合

//$now=2018-06-30 18:00:00
$now = Carbon::now();  //2018-06-30 18:00:00
//$now=2018-07-01 18:00:00
$now->addDay(); //2018-07-01 18:00:00
//$now=2018-07-31 18:00:00
$now->endOfMonth();

Carbon(1系)の場合、一度作ったオブジェクトは、ミュータブルです(2系から一度作ったオブジェクトがイミュータブルとなりました。)。なので、一度作ったオブジェクトの状態が変わります。
しかし、Chronosの場合は、一度作ったオブジェクトは、イミュータブルでオブジェクトの状態が変わることがありません。
下記に例を示します。

Chronosの場合

//$now=2018-06-30 18:00:00
$now = Chronos::now();

//$now=2018-06-30 18:00:00
//$tomorrow=2018-07-01 18:00:00
$tommorow = $now->addDay();

//$now=2018-06-30 18:00:00
//$end_of_month=2018-07-31 18:00:00
$end_of_month = $now->endOfMonth();

Carbon(1系)であると、代入した値と代入前のオブジェクトが同じ値となり、バグの生む可能性が起こりますが、Chronosであると代入した値と代入前の状態が異なります。このことがバグの生む可能性を少なくします。

CakePHP3.2.0でCakePHPがCarbonからChronosへ乗り換えたのは、Chronosがイミュータブルであることがあると言われています。

Chronosのインストール

Chronosのインストールは、Composerで行います。Composerをインストールされていない場合は、インストールしてください。
=>Composerのインストール

$ composer require cakephp/chronos

これでChronosのインストールは終了です。下記の様にChronosを読み込むことで、Chronosを使用することができます。

<?php
require 'vendor/autoload.php';

use Cake\Chronos\Chronos;

printf("Now: %s", Chronos::now());

CakePHP3.2以上でのChronosの使用方法は、公式サイトを参照ください。

Chronosの使用例

単体テストでの利用

Chronosを使用すると、過去のある瞬間に固定した場合、新たな Chronos のインスタンスが生成する now または相対時刻の文字列は、 固定された時刻の相対を返却します。これにより、モックして特定の日時を返すようにするテストをする必要がなくなります。

Chronos::setTestNow(new Chronos('1975-12-25 00:00:00'));

//$time=1975-12-25 00:00:00
$time = new Chronos(); 
//$time=1975-12-24 23:00:00
$time = new Chronos('1 hour ago');
Chronosでの日付のフォーマット
$chronos = Chronos::create(2018, 5, 30, 11, 22, 33);
echo $chronos; // 2018-05-30 11:22:33
echo $chronos->format('Y年m月d日'); // 2018年05月30日
 
$chronos->setToStringFormat('Y/m/d H:i:s'); // デフォルトフォーマットを変更
echo $chnoros; // 2018年05月30日
 
$chronos->resetToStringFormat(); //フォーマットをリセットしデフォルトに戻す
echo $chronos; // 2018-05-30 11:22:333
現在日時や指定日時の取得
//現在のシステム日時
$chronos = new Chronos();
echo $chronos;                           // 2018-06-26 19:22:02 Chronos::now()と同じ
//現在のシステム日時
$now = Chronos::now();
echo $now;                               // 2018-06-26 19:22:02
//今日
$today = Chronos::today();
echo $today;                             // 2018-06-26 00:00:00
//東京の明日
$tomorrow = Chronos::tomorrow('Asia/Tokyo');
echo $tomorrow;                          // 2018-06-28 00:00:00
//昨日
$yesterday = Chronos::yesterday();
echo $yesterday;                         // 2018-06-25 00:00:00
//日時を指定
$parse = Chronos::parse('2018-04-30 10:30:10');
echo $parse;                             // 2018-04-30 10:30:10
Chronosインスタンスから必要な日時を取得
$dt = Chronos::parse('2012-9-5 23:26:11.123789');

// These getters specifically return integers, ie intval()
//年を取得
echo $dt->year;                          // 2012
//月を取得
echo $dt->month;                         // 9
//日を取得
echo $dt->day;                           // 5
//時刻を取得
echo $dt->hour;                          // 23
//分を取得
echo $dt->minute;                        // 26
//廟を取得
echo $dt->second;                        // 11
//マイクロ秒を取得
echo $dt->micro;                         // 123789

// 曜日を0(日) から 6 (土)で取得
echo $dt->dayOfWeek;                     //3

//年のうちその日が何日経った日かを取得
echo $dt->dayOfYear;
//その日が月の何週目かを取得
echo $dt->weekOfMonth;
//weekOfMonthは月曜日から日曜日までの週を考慮しているため、
//週何週目かは1日が始まる曜日により変わります。日曜日から始まる場合は1日、
//月曜日から始まる場合は7日までが1週目となります。

//年で見た際の経過週
echo $dt->weekOfYear;                  //36
//月の日数
echo $dt->daysInMonth;                 //30
//UNIXタイムスタンプ
echo $dt->timestamp;                  //1346901971
//Chronos::now()の時点で何歳か
echo Chronos::createFromDate(1975, 5, 21)->age; // 42
//四半期
echo $dt->quarter;                    // int(3)
// タイムゾーン名
echo $dt->tzName;                     // Asia/Tokyo
日付の判定
//現在のシステム時間を取得(ここの値により下記の判定は変わります)
$dt = Chronos::now();
//$dt2 = Chronos::parse('2012-9-5 23:26:11.123789');

//boolean
//$dtが未来かどうか
$dt->isFuture();
//$dtが過去かどうか
$dt->isPast();

//$dtが今年かどうか
$dt->isThisYear();
//$dtが来年かどうか
$dt->isNextYear();
//$dtが去年かどうか
$dt->isLastYear();
//$dtが閏年かどうか
$dt->isLeapYear();
//$dtが現在の月かどうか
$dt->isThisMonth();
//$dtが来月かどうか
$dt->isNextMonth();
//$dtが先月かどうか
$dt->isLastMonth();

//平日かどうか(デフォルトでは、日本の祝日は未対応だと思います)
$dt->isWeekday();
//週末かどうか(デフォルトだと土日らしい)
$dt->isWeekend();
//月曜かどうか
$dt->isMonday();
//火曜かどうか
$dt->isTuesday();
//水曜かどうか
$dt->isWednesday();
//木曜かどうか
$dt->isThursday();
//金曜かどうか
$dt->isFriday();
//土曜かどうか
$dt->isSaturday();
//日曜かどうか
$dt->isSunday();

//昨日かどうか
$dt->isYesterday();
//今日かどうか
$dt->isToday();
//明日かどうか
$dt->isTomorrow();
//来週かどうか
$dt->isNextWeek();
//先週かどうか
$dt->isLastWeek();

// 値が相対的な期間内にあったかどうかを見つけることができます。
$dt->wasWithinLast('3 days');
$dt->isWithinNext('3 hours');
日付の比較
$first = new Chronos('2018-05-01');
$second = new Chronos('2018-06-01');

//イコール
var_dump($first->eq($second));                     // bool(false)
//ノットイコール
var_dump($first->ne($second));                     // bool(true)
//より大きい
var_dump($first->gt($second));                     // bool(false)
//以上
var_dump($first->gte($second));                    // bool(false)
//より小さい
var_dump($first->lt($second));                     // bool(true)
//以下
var_dump($first->lte($second));                    // bool(true)

//間
var_dump(Chronos::create(2018, 5, 10, 3)->between($first, $second)); // bool(true)
日付の加算減算
$dt = Chronos::create(2012, 1, 31, 0);

echo $dt->toDateTimeString();            // 2012-01-31 00:00:00

//年の加算
echo $dt->addYears(5);                   // 2017-01-31 00:00:00
//1年の加算
echo $dt->addYear();                     // 2018-01-31 00:00:00
//年の減算
echo $dt->subYear();                     // 2017-01-31 00:00:00
//年の減算
echo $dt->subYears(5);                   // 2012-01-31 00:00:00
echo $dt->addMonths(60);                 // 2017-01-31 00:00:00
//1ヶ月の加算
echo $dt->addMonth();                    // 2017-03-03 00:00:00 と同じ $dt->month($dt->month + 1);

//1ヶ月の減算
echo $dt->subMonth();                    // 2017-02-03 00:00:00
//月の減算
echo $dt->subMonths(60);                 // 2012-02-03 00:00:00

//日付の加算
echo $dt->addDays(29);                   // 2012-03-03 00:00:00
//1日の追加
echo $dt->addDay();                      // 2012-03-04 00:00:00
//1日の減算
echo $dt->subDay();                      // 2012-03-03 00:00:00
//日付の減算
echo $dt->subDays(29); 

//平日の加算
echo $dt->addWeekdays(4);                // 2012-02-09 00:00:00
//平日1日の加算
echo $dt->addWeekday();                  // 2012-02-10 00:00:00
//平日1日の減算
echo $dt->subWeekday();                  // 2012-02-09 00:00:00
//平日の減算
echo $dt->subWeekdays(4);                // 2012-02-03 00:00:00

//週の加算
echo $dt->addWeeks(3);                   // 2012-02-24 00:00:00
//1週間の加算
echo $dt->addWeek();                     // 2012-03-02 00:00:00
//1週間の減算
echo $dt->subWeek();                     // 2012-02-24 00:00:00
//週の減算
echo $dt->subWeeks(3);                   // 2012-02-03 00:00:00
//時間の加算
echo $dt->addHours(24);                  // 2012-02-04 00:00:00
//1時間の加算
echo $dt->addHour();                     // 2012-02-04 01:00:00
//1時間の減算
echo $dt->subHour();                     // 2012-02-04 00:00:00
//時間の減算
echo $dt->subHours(24);                  // 2012-02-03 00:00:00

//分の加算
echo $dt->addMinutes(61);                // 2012-02-03 01:01:00
//1分の加算
echo $dt->addMinute();                   // 2012-02-03 01:02:00
//1分の減算
echo $dt->subMinute();                   // 2012-02-03 01:01:00
//分の減算
echo $dt->subMinutes(61);                // 2012-02-03 00:00:00

//秒の加算
echo $dt->addSeconds(61);                // 2012-02-03 00:01:01
//1秒の加算
echo $dt->addSecond();                   // 2012-02-03 00:01:02
//1秒の減算
echo $dt->subSecond();                   // 2012-02-03 00:01:01
//秒の減算
echo $dt->subSeconds(61);                // 2012-02-03 00:00:00
日付の差分

$date1 = Chronos::create(2012, 1, 31, 0);
$date2 = Chronos::create(2018, 6, 29, 0);

// 現在日時からの差を取得
Chronos::fromNow($date1);

//秒
echo $date1->diffInSeconds($date2);                 // 202262400 
//分
echo $date1->diffInMinutes($date2);                 // 3371040
//時間
echo $date1->diffInHours($date2);                   // 56184
//日にち
echo $date1->diffInDays($date2);                   // 2341
//週
echo $date1->diffInWeeks($date2);                  // 334
//月
echo $date1->diffInMonths($date2);                 // 76
//年
echo $date1->diffInYears($date2);                  // 6

ほとんど、利用方法は、Carbonと同じ様です。

Chronosを使うかCarbonを使うか悩んでいる方。Chronosには、イミュータブルという特徴があります。
Chronosを日付時刻を扱うライブラリーの一つの選択肢として考えても良いかもしれません。