LoginSignup
3
3

More than 3 years have passed since last update.

Laravelで和暦を扱う

Last updated at Posted at 2020-08-04

和暦を扱う必要があったので,その実装をまとめてみました

環境

  • Laravel 5.8

作るもの

  • 年号を管理するテーブル(era_names)
  • 和暦クラス
  • 和暦と西暦の変換処理

年号テーブルの作成

年号を管理するテーブルには

  • 年号の名前
  • 略称
  • 開始日
  • 終了日

の4つのカラムを作成します.テーブル名をera_namesとし,マイグレーションファイルは次のようになります.

public function up()
{
    Schema::create('era_names', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('short_name');
        $table->date('start_at');
        $table->date('end_at');
        $table->timestamps();
    });
}

作成したマイグレーションを実行しテーブルを作成したら,モデルを作成します.

app\Models\EraName.php
class EraName extends Model
{
    /** @var string */
    protected $table = 'era_names';

    /** @var array */
    protected $dates = [
        'start_at',
        'end_at',
    ];

    /** @var array */
    protected $guarded = [
        'id',
    ];
}

最後にデータを追加します.追加する方法はシーダやtinkerを用いてください.

name short_name start_at end_at
昭和 S 1926-12-25 1989-01-07
平成 H 1989-01-08 2019-04-30
令和 R 2019-05-01 2100-01-01

和暦-西暦変換処理の作成

プロジェクトの方針にしたがって,和暦-西暦の変換処理を作成します.ここでは和暦クラスを作成し,Carbon(CarbonImmutable)と相互変換する形で実装します.他の実装として変換処理をすべてServiceに書き,ファサードやヘルパを通して変換することなどが考えられます.

Carbonに和暦への変換メソッドを実装するために,次のようなサービスプロバイダを用意します.

app\Providers\DateServiceProvider
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Date;
use App\Models\EraName;
use App\Models\Wareki;
use Carbon\CarbonImmutable;

class DateServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        Date::use(CarbonImmutable::class);  // 日付を扱うクラスをCarbonImmutableに変更

        Date::macro('toWareki', function () {  // 日付を扱うクラスにtoWarekiメソッドを追加
            return new Wareki($this);
        });
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Wareki::setEraNames();
    }
}

Date::macro(...)を用いることで,日付オブジェクトにメソッドを追加することができます.ここではtoWarekiメソッドを追加しています.

Wareki::setEraNames();

の部分では下で定義するWarekiクラスにstaticプロパティとしてEraName::all()で取得できる年号テーブルの一覧をセットしています.staticとして年号一覧を持つことで,処理の途中でera_namesテーブルの内容が変更されても,処理開始時の年号データが使われるようになります.また,年号を取得するSQLが1度しか実行されなくなるなどの利点があります.

次に和暦クラスを実装します.場所はプロジェクトの方針にしたがいましょう.ここではapp\Models下に作成します.

app\Models\Wareki.php
<?php

namespace App\Models;

use Illuminate\Support\Facades\Date;
use App\Models\EraName;

class Wareki
{
    /** @var collection */
    private static $eraNames;

    /** @var int */
    public $year;  // 年
    public $fiscalYear;  // 年度
    public $month;
    public $day;

    /** @var EraName */
    public $eraName;

    public static function setEraNames() {
        if (is_null(self::$eraNames)) self::$eraNames = EraName::all();
    }

    /**
     * @param Carbon|CarbonImmutable
     */
    public function __construct($ymd) {
        $this->eraName = self::$eraNames->first(function ($eraName) use ($ymd) {
            return $eraName->start_at->startOfDay() <= $ymd && $ymd <= $eraName->end_at->endOfDay();
        });

        $this->year = $ymd->year - $this->eraName->start_at->year + 1;
        $this->fiscalYear = $this->year - ((1 <= $ymd->month && $ymd->month <= 3) ? 1 : 0);
        $this->month = $ymd->month;
        $this->day = $ymd->day;
    }

    /**
     * @return Carbon|CarbonImmutable
     */
    public function toYmd() {
        return Date::createMidnightDate(
            $this->eraName->start_at->year + $this->year - 1,
            $this->month ?? 1,
            $this->day ?? 1,
        );
    }

    /**
     * @param string $format
     * @return string
     */
    public function format(string $format) {
        $ret = '';
        foreach (mb_str_split($format) as $str) {
            if ($str === 'Y') $ret .= sprintf('%s%02d', $this->eraName->name, $this->year);
            else if ($str === 'S') $ret .= sprintf('%s%02d', $this->eraName->short_name, $this->year);
            else if ($str === 'y') $ret .= sprintf('%s%02d', $this->eraName->name, $this->fiscalYear);
            else if ($str === 's') $ret .= sprintf('%s%02d', $this->eraName->short_name, $this->fiscalYear);
            else if ($str === 'm') $ret .= sprintf('%02d', $this->month);
            else if ($str === 'd') $ret .= sprintf('%02d', $this->day);
            else $ret .= $str;
        }

        return $ret;
    }
}

和暦-西暦の変換処理は和暦クラスにまとめることにしてあります.コンストラクタで西暦->和暦の変換を行い,toYmdメソッドで和暦->西暦の変換を行います.formatメソッドでは和暦を文字列に変換します.例えば

$ymd = Date::parse('2019-03-01');  \\ 平成年31年3月1日

$ymd->toWareki()->format('Y年');  \\ 平成31年
$ymd->toWareki()->format('S');  \\ H31
$ymd->toWareki()->format('y年度');  \\ 平成30年度
$ymd->toWareki()->format('s');  \\ H30
$ymd->toWareki()->format('m月d日');  \\ 03月01日

このようになっています.この和暦クラスでは時分秒を扱わない形で実装しています.

まとめ

Laravelで和暦を扱ってみました.年号データをDBではなくconfigなどに設定する場合も,setEraNamesの部分を調整することで実装することができます.また,Data::macroを用いて年度初めを取得する処理などを追加してもいいかもしれません.

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3