和暦や日本語の曜日表記に対応したフォーマット変更プログラムが欲しい!
日付のフォーマットを変更したい時、dateTimeオブジェクトのインスタンスを作成してフォーマット変更して出力していますが、日本人向けにシステムを作成しているとどうしても使いたくなるのが、日本語表示。
- 和暦→西暦、西暦→和暦の変更がしたい
- 日本語の曜日表示を使いたい
- dateTimeのフォーマット文字列も網羅したい
こんなことを考えている方は多いのではないでしょうか。
世の中には、日本語表示に対応した関数などを作ってくれている方はたくさんいるので、分かりやすいのを自分で選んで使ってもらいたいですが、本記事では
こんな方に向けて、今回開発したコードとコードの解説をしています。
使ってみたい方は、コードをコピペで使ってもらってOKです。
もしコードの内容に興味があって、自分でも構築してみたいという方は、コード解説を書いている記事がありますので、こちらの記事を参考にしてみてください。
開発経緯
職員より患者検索を生年月日で行う時、どんな入力形式でもヒットするような検索機能が欲しいとの要望がありました。
例えば、下記の様な検索文字列です。
- h111
- 平成2年1月1日
- 20001231
電話対応で、患者様は西暦でおっしゃることもあれば、和暦でおっしゃる方もいます。
また、メールなどからコピー&ペーストで検索を実行したい時などがあり、生年月日の検索文字列は、かなりばらつきがあります。
これまでは、和暦から西暦にする関数、西暦から和暦にする関数などを作成し対応していました。
しかし、
- 開発中に使う関数が増える
- 和暦、曜日などを組み合わせたフォーマットを作成する時複雑になる
- フォーマットを変更するときの変更箇所が増える
などの理由から、もっと簡単にフォーマットを変更できる方法はないかと考えたとき、じゃあ自分で使いやすい日付フォーマット変更のプログラムを作ろうということで、今回のプログラムを作成しました。
機能要件
開発するにあたり、機能要件として下記の点を網羅するプログラムを目指しました。
- dateTimeオブジェクトのような動きを再現
- 入力する日付は和暦でも西暦でも機能すること
- 入力する日付の形式は下記のようなものを想定すること
- 平成元年12月31日
- 平成2年1月1日
- h11231
- H11231
- r010501
- 月や日付を省略できるようにすること
- フォーマット指定は和暦、元年表示も可能にすること
- 日本語表記の曜日を指定出ること
完成コードと使用方法|使用例も紹介
まずは、完成したコードと、実際に使用してみたときの出力結果をお見せします。
その後で実装方法と各コードの解説をしますので、コードの内容にまで興味がある方は最後まで読んで見てください。
とりあえず使えればいいという方は、実装方法を読んでコピペして使ってください。
完成コード
<?php
class FormatDate
{
private $date_time;
private $year;
private $era_lists = [
// 令和(2019年5月1日〜)
[
'jp' => '令和',
'jp_abbr' => '令',
'en' => 'r',
'en_abbr' => 'R',
'time' => '20190501',
'year' => '2019'
],
// 平成(1989年1月8日〜)
[
'jp' => '平成',
'jp_abbr' => '平',
'en' => 'h',
'en_abbr' => 'H',
'time' => '19890108',
'year' => '1989'
],
// 昭和(1926年12月25日〜)
[
'jp' => '昭和',
'jp_abbr' => '昭',
'en' => 's',
'en_abbr' => 'S',
'time' => '19261225',
'year' => '1926'
],
// 大正(1912年7月30日〜)
[
'jp' => '大正',
'jp_abbr' => '大',
'en' => 't',
'en_abbr' => 'T',
'time' => '19120730',
'year' => '1912'
],
// 明治(1873年1月1日〜)
// ※明治5年以前は旧暦を使用していたため、明治6年以降から対応
[
'jp' => '明治',
'jp_abbr' => '明',
'en' => 'm',
'en_abbr' => 'M',
'time' => '18730101',
'year' => '1873'
],
];
public function __construct($time = 'now', $timezone = null) {
$time = preg_replace("/\\s| /", "", mb_convert_kana($time, 'n'));
if(preg_match('/^(令|令和|r|R|平|平成|h|H|昭|昭和|s|S|大|大正|t|T|明|明治|m|M)?(元|0?[1-9])?(年|-|\\/?)?(0?[1-9]|1[0-2])?(月|-|\\/?)?(0?[1-9]|[12][0-9]|3[01])?(日?)?$/', $time, $matches)) {
$era = $matches[1];
$year = intval(str_replace('元', '1',$matches[2]));
$this->year = sprintf('%02d', $year);
$month = !empty($matches[4]) ? intval($matches[4]) : 1;
$day = !empty($matches[6]) ? intval($matches[6]) : 1;
foreach ($this->era_lists as $era_list) {
if($era_list['jp'] === $era || $era_list['jp_abbr'] === $era || $era_list['en'] === $era || $era_list['en_abbr'] === $era){
$seireki_year = $era_list['year'] + $year - 1;
$seireki = sprintf('%04d-%02d-%02d', $seireki_year, $month, $day);
$this->date_time = new DateTime($seireki, $timezone);
break;
}
}
} else {
$this->date_time = new DateTime($time, $timezone);
}
}
public function format($format) {
/*
@param string $format 'K':元号
'k':元号略称
'Q':元号(英語表記)
'q':元号略称(英語表記)
'X':和暦年(前ゼロ表記)
'x':和暦年
'R':曜日
'V':曜日略称
'f':元(元年表記)
@param string $this->date_time 変換対象となる日付(西暦)
@return string $result 変換後の日付(和暦)
*/
$week = ['日','月','火','水','木','金','土'];
$week_long = ['日曜日','月曜日','火曜日','水曜日','木曜日','金曜日','土曜日'];
$format_K = '';
$format_k = '';
$format_Q = '';
$format_q = '';
$format_X = $this->date_time->format('Y');
$format_x = $this->date_time->format('y');
$format_R = $week_long[$this->date_time->format('w')];
$format_V = $week[$this->date_time->format('w')];
$format_f = '元';
foreach ($this->era_lists as $era_list) {
$date_era = new DateTime($era_list['time']);
if ($this->date_time->format('Ymd') >= $date_era->format('Ymd')) {
$format_K = $era_list['jp'];
$format_k = $era_list['jp_abbr'];
$format_Q = $era_list['en'];
$format_q = $era_list['en_abbr'];
$format_x = $this->date_time->format('Y') - $date_era->format('Y') + 1;
$format_X = sprintf('%02d', $format_x);
break;
}
}
$result = '';
foreach (str_split($format) as $value) {
// フォーマットが指定されていれば置換する
if (isset(${"format_{$value}"}) && $value === 'f' && $this->year === '01'){
$result .= ${"format_{$value}"};
} elseif(isset(${"format_{$value}"}) && $value === 'f' && $this->year !== '01'){
$year_value = 'X';
$result .= ${"format_{$year_value}"};
} elseif (isset(${"format_{$value}"}) && $value !== 'f') {
$result .= ${"format_{$value}"};
} else {
$result .= $this->date_time->format($value);
}
}
return $result;
}
}
使用方法と使用例
使用方法は非常に簡単です。
FormatDateはクラスで作成されていますので、インスタンスを作成
$format_date = new FormatDate('$date');
format()関数を呼び出して、フォーマットを指定するだけです。
$format_date->format('Y-m-d');
実際に使ってみたいと思います。
インスタンスを作成して、日付は「令和1年5月1日」を指定します。
$format_date = new FormatDate('令和1年5月1日');
それぞれ指定したフォーマットの出力結果は下記のようになります。
$format_date->format('Y-m-d'); //2019-05-01
$format_date->format('KX年m月d日'); //令和01年05月01日
$format_date->format('Kf年m月d日'); //令和元年05月01日
$format_date->format('qXmd'); //R010501
$format_date->format('R'); //水曜日
指定できるフォーマットは、dateTimeのformat関数で指定できるものに加えて、下記のフォーマットに対応できるように作成しています。
まとめ
世の中には、もっと便利な日付をフォーマットする関数を紹介している方がいるかもしれないですが、勉強用と自分で使用するための分かりやすいフォーマット変更のオブジェクトを作成することができました。
作成したFormatDateオブジェクトが誰かの役に立てれば幸いです。
このクラスを作成するにあたり、下記の記事を参考にさせていただきました。
またコード解説を読みたい方はこちらの記事も読んでみてください。