LoginSignup
20
17

More than 5 years have passed since last update.

PHPで和暦・日本語曜日を使う DateTime拡張版

Last updated at Posted at 2015-04-14

PHPで和暦・日本語曜日を使うのDateTimeクラス拡張版を作ってみました。
お好きな方をお使いください。
フォーマットに使えるものも変わりありません。

注意

明治5年以前は天保暦・寛政暦などが利用されており、現在のグレゴリオ暦と日付が一致していません。
そのため、和暦の年号(K,k)のオプションは明治5年以前では正しく出ない場合があります。
日付に関してもグレゴリオ暦のものが表示されるため、和暦としての表示と異なります。
「JK年n月j日」が正しく動作するのは明治6年以降となりますのでご注意ください。
例:西暦 1834年6月1日 => 実際の和暦 天保5年4月24日

@tadsanさん、情報・ご指摘ありがとうございます。

ソース

<?php
namespace MyLibrary;

/**
 * 日本語対応DateTime拡張クラス
 * 
 * 元号、曜日などの日本語表記に対応したDateTime拡張クラス
 * 
 * @version 1.0.1
 * @author chiyoyo
 * @caution PHP5.2以上
 */
class DateTimeJp extends \DateTime
{
    // フォーマット定義追加
    const JP_DATE = 'JK年n月j日'; // 例:平成元年5月7日(明治5年以前は当時と異なる日付が出るので注意)
    const JP_TIME = 'Eg時i分s秒'; // 例:午後3時05分07秒

    const DEFAULT_TO_STRING_FORMAT = 'Y-m-d H:i:s'; // toString()で利用する表示フォーマット

    /**
     * 元号用設定
     * 日付はウィキペディアを参照しました
     * 
     * @see http://ja.wikipedia.org/wiki/%E5%85%83%E5%8F%B7%E4%B8%80%E8%A6%A7_%28%E6%97%A5%E6%9C%AC%29 元号一覧 (日本)
     */
    private static $gengoList = [
        ['name' => '令和', 'name_short' => 'R', 'timestamp' =>  1556636400],  // 2019-05-01,
        ['name' => '平成', 'name_short' => 'H', 'timestamp' =>  600188400],  // 1989-01-08,
        ['name' => '昭和', 'name_short' => 'S', 'timestamp' => -1357635600], // 1926-12-25'
        ['name' => '大正', 'name_short' => 'T', 'timestamp' => -1812186000], // 1912-07-30
        ['name' => '明治', 'name_short' => 'M', 'timestamp' => -3216790800], // 1868-01-25
    ];

    /** 日本語曜日設定 */
    private static $weekJp = [
        0 => '日',
        1 => '月',
        2 => '火',
        3 => '水',
        4 => '木',
        5 => '金',
        6 => '土',
    ];
    /** 午前午後 */
    private static $ampm = [
        'am' => '午前',
        'pm' => '午後',
    ];

    /**
     * 文字列に変換された際に返却するもの
     *
     * @return string
     */
    public function __toString()
    {
        return $this->format(self::DEFAULT_TO_STRING_FORMAT);
    }

    /**
     * 和暦などを追加したformatメソッド
     *
     * 追加した記号
     * J : 元号
     * b : 元号略称
     * K : 和暦年(1年を元年と表記)
     * k : 和暦年
     * x : 日本語曜日(0:日-6:土)
     * E : 午前午後
     * 
     * @param string $format DateTime::formatに準ずるformat文字列
     * @return string
     */
    public function format($format)
    {
        // 和暦関連のオプションがある場合は和暦取得
        $gengo = array();
        if (preg_match('/(?<!\\\)[J|b|K|k]/', $format)) {
            foreach (self::$gengoList as $g) {
                if ($g['timestamp'] <= $this->getTimestamp()) {
                    $gengo = $g;
                    break;
                }
            }
            // 元号が取得できない場合はException
            if (empty($gengo)) {
                throw new Exception('Can not be converted to a timestamp : '.$this->getTimestamp());
            }
        }

        // J : 元号
        if ($this->isCharactor('J', $format)) {
            $format = $this->replaceCharactor('J', $gengo['name'], $format);
        }

        // b : 元号略称
        if ($this->isCharactor('b', $format)) {
            $format = preg_replace('/b/', '¥¥' . $gengo['name_short'], $format);
        }

        // K : 和暦用年(元年表示)
        if ($this->isCharactor('K', $format)) {
            $year = date('Y', $this->getTimestamp()) - date('Y', $gengo['timestamp']) + 1;
            $year = $year == 1 ? '元' : $year;
            $format = $this->replaceCharactor('K', $year, $format);
        }

        // k : 和暦用年
        if ($this->isCharactor('k', $format)) {
            $year = date('Y', $this->getTimestamp()) - date('Y', $gengo['timestamp']) + 1;
            $format = $this->replaceCharactor('k', $year, $format);
        }

        // x : 日本語曜日
        if ($this->isCharactor('x', $format)) {
            $w = date('w', $this->getTimestamp());
            $format = $this->replaceCharactor('x', self::$weekJp[$w], $format);
        }

        // 午前午後
        if ($this->isCharactor('E', $format)) {
            $a = date('a', $this->getTimestamp());
            $value = isset(self::$ampm[$a]) ? self::$ampm[$a] : '';
            $format = $this->replaceCharactor('E', $value, $format);
        }

        return parent::format($format);
    }

    /**
     * 指定した文字があるかどうか調べる(エスケープされているものは除外)
     * @param string $char 検索する文字
     * @param string $string 検索される文字列
     * @return boolean
     */
    private function isCharactor($char, $string)
    {
        return preg_match('/(?<!\\\)'.$char.'/', $string);
    }

    /**
     * 指定した文字を置換する(エスケープされていないもののみ)
     * @param string $char 置換される文字
     * @param string $replace 置換する文字列
     * @param string $string 元の文字列
     * @return string 置換した文字列
     */
    private function replaceCharactor($char, $replace, $string)
    {
        // エスケープされていないもののみ置換
        $string = preg_replace('/(?<!\\\)'.$char.'/', '${1}'.$replace, $string);
        // エスケープ文字を削除
        $string = preg_replace('/\\\\'.$char.'/', $char, $string);
        return $string;
    }
}

参考

生活や実務に役立つ高精度計算サイト

おまけ

利用できない元号をおまけとして載せてみます。
実際は天保歴などを計算しなければならないため、タイムスタンプが異なります。
以下は現在の暦から計算しただけのタイムスタンプです。

明治以前の元号
    private static $gengoList = [
        // 江戸時代
        ['name' => '慶応', 'timestamp' => -3303104400], // 1865-05-01
        ['name' => '元治', 'timestamp' => -3337664400], // 1864-03-27
        ['name' => '文久', 'timestamp' => -3432186000], // 1861-03-29
        ['name' => '万延', 'timestamp' => -3462858000], // 1860-04-08
        ['name' => '安政', 'timestamp' => -3627882000], // 1855-01-15
        ['name' => '嘉永', 'timestamp' => -3842154000], // 1848-04-01
        ['name' => '弘化', 'timestamp' => -3943933200], // 1845-01-09
        ['name' => '天保', 'timestamp' => -4384573200], // 1831-01-23
        ['name' => '文政', 'timestamp' => -4784173200], // 1818-05-26
        ['name' => '文化', 'timestamp' => -5231552400], // 1804-03-22
        ['name' => '享和', 'timestamp' => -5326506000], // 1801-03-19
        ['name' => '寛政', 'timestamp' => -5707530000], // 1789-02-19
        ['name' => '天明', 'timestamp' => -5954374800], // 1781-04-25
        ['name' => '安永', 'timestamp' => -6218586000], // 1772-12-10
        ['name' => '明和', 'timestamp' => -6485130000], // 1764-06-30
        ['name' => '宝暦', 'timestamp' => -6881014800], // 1751-12-14
        ['name' => '寛延', 'timestamp' => -6986941200], // 1748-08-05
        ['name' => '延享', 'timestamp' => -7123885200], // 1744-04-03
        ['name' => '寛保', 'timestamp' => -7217802000], // 1741-04-12
        ['name' => '元文', 'timestamp' => -7370730000], // 1736-06-07
        ['name' => '享保', 'timestamp' => -7996438800], // 1716-08-09
        ['name' => '正徳', 'timestamp' => -8159389200], // 1711-06-11
        ['name' => '宝永', 'timestamp' => -8385066000], // 1704-04-16
        ['name' => '元禄', 'timestamp' => -8873485200], // 1688-10-23
        ['name' => '貞享', 'timestamp' => -9017082000], // 1684-04-05
        ['name' => '天和', 'timestamp' => -9092941200], // 1681-11-09
        ['name' => '延宝', 'timestamp' => -9346266000], // 1673-10-30
        ['name' => '寛文', 'timestamp' => -9738781200], // 1661-05-23
        ['name' => '万治', 'timestamp' => -9825699600], // 1658-08-21
        ['name' => '明暦', 'timestamp' => -9928602000], // 1655-05-18
        ['name' => '承応', 'timestamp' => -10009818000], // 1652-10-20
        ['name' => '正保', 'timestamp' => -10254934800], // 1645-01-13
        ['name' => '寛永', 'timestamp' => -10909501200], // 1624-04-17
        ['name' => '元和', 'timestamp' => -11181402000], // 1615-09-05
        // 安土桃山時代
        ['name' => '慶長', 'timestamp' => -11772118800], // 1596-12-16
        ['name' => '文禄', 'timestamp' => -11896189200], // 1593-01-10
        ['name' => '天正', 'timestamp' => -12507728400], // 1573-08-25
        // 戦国時代
        ['name' => '元亀', 'timestamp' => -12610198800], // 1570-05-27
        ['name' => '永禄', 'timestamp' => -12994938000], // 1558-03-18
        ['name' => '弘治', 'timestamp' => -13069414800], // 1555-11-07
        ['name' => '天文', 'timestamp' => -13801222800], // 1532-08-29
        ['name' => '享禄', 'timestamp' => -13927021200], // 1528-09-03
        ['name' => '大永', 'timestamp' => -14146218000], // 1521-09-23
        ['name' => '永正', 'timestamp' => -14699178000], // 1504-03-16
        ['name' => '文亀', 'timestamp' => -14793699600], // 1501-03-18
        ['name' => '明応', 'timestamp' => -15064909200], // 1492-08-12
        ['name' => '延徳', 'timestamp' => -15156579600], // 1489-09-16
        ['name' => '長享', 'timestamp' => -15223021200], // 1487-08-09
        ['name' => '文明', 'timestamp' => -15796371600], // 1469-06-08
        ['name' => '応仁', 'timestamp' => -15864714000], // 1467-04-09
    ];

更新履歴

日付 更新内容
2015/04/14 エスケープすることで置換しないように修正
2016/01/15 日本語曜日出力のバグ修正
20
17
6

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
20
17