和暦と西暦を変換する必要がある時がしばしばあります。最近の和暦のみであれば変換は比較的シンプルです。単に元号の切り替わった日付を保持し、その切り替わった範囲を目安にして年を変換すればそれでおしまいです。しかしながら古くなるにつれて元号が切り替わった日が何時なのか調べるのが億劫だったり、暦の数え方が現代と違うことで月日の方も変換する必要があったりと大分面倒です。これを解決した暦の変換をできるのが国立天文台暦計算室が公開している「日本の暦日データベース」です。
使い方はシンプルです。設定欄にある入力欄にやりたいことを書いて表示、ないしCSVでダウンロードするだけです。
もし自作のシステムの内部などで頻繁に計算する必要がある場合、CSVダウンロードを利用してローカルにデータベースを持った方が無難です。過度のアクセスは迷惑行為です。例えば次のPHPコードでCSVをダウンロードして、一つの変換カレンダーにできます。この出来上がった変換カレンダーをより高速に検索できるように保存(KeyValueストア、RDB、ファイルを分割してパスに年月日を含めるなど)し、都度読み出せばいい感じになります。
<?php
/**
* 指定された年のカレンダーデータを取得する関数
* @param int $year 取得するカレンダーの年
* @param string $dir 保存先ディレクトリ
* @return string 'WRITE' | 'SKIP' 処理結果
*/
function get_calendar(int $year, string $dir): string
{
// 指定された年のカレンダーファイルが既に存在するかチェック
if (file_exists($dir . DIRECTORY_SEPARATOR . $year . '.csv')) {
// 存在する場合は上書きしない
return 'SKIP';
}
// cURLセッションを初期化
$ch = curl_init();
// リクエストのURLとPOSTデータを設定
$url = "https://eco.mtk.nao.ac.jp/cgi-bin/koyomi/caldb.cgi";
$postData = "type=0&gengo=a000&year={$year}&month=1&day=1&jg=2&div=1&divu=3&len=366&lenu=3&gen=1&csv=CSV";
// cURLオプションを設定
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/x-www-form-urlencoded",]);
// リクエストを実行してレスポンスを取得
$response = curl_exec($ch);
// エラーチェック
if ($response === false) {
die('エラーが発生しました: ' . curl_error($ch));
}
curl_close($ch);
// レスポンスをファイルとして保存
$fileName = $dir . DIRECTORY_SEPARATOR . $year . '.csv';
file_put_contents($fileName, $response);
return 'WRITE';
}
/**
* メイン関数。リクエスト投げてカレンダーファイルを保存
* @param int $startYear 開始年
* @param int $endYear 終了年
* @param string|null $saveDir 保存先ディレクトリ。デフォルトは現在のディレクトリをルートとした/tmp/calendar/
* @return void
*/
function main(int $startYear, int $endYear, string|null $saveDir = null): void
{
$saveDir = $saveDir ?: __DIR__ . '/tmp/calendar/';
// 保存ディレクトリが存在しない場合は作成
if (!file_exists($saveDir)) {
mkdir($saveDir, 0777, true);
}
// 指定された期間のカレンダーを取得
for ($year = $startYear; $year <= $endYear; $year++) {
$action = get_calendar($year, $saveDir);
if ($action === 'SKIP') {
echo "SKIP: {$year}\n";
} else {
echo "WRITE: {$year}\n";
sleep(1); // 通信した場合は次のリクエストまで1秒待機
}
}
// 保存したカレンダーファイルを結合
$merged = [];
for ($year = $startYear; $year <= $endYear; $year++) {
$fileName = $saveDir . DIRECTORY_SEPARATOR . $year . '.csv';
if (!file_exists($fileName)) {
continue;
}
$lines = file($fileName);
foreach ($lines as $line) {
$merged[$line] = $line; // 重複行を除外するため、キーに行を設定
}
}
// 時系列順にソート。1列目が西暦なのでそのままソートするだけでOK
sort($merged);
// 結合したデータをUTF-8に変換し、下に行ったヘッダ行のつじつまを合わせる
$content = implode('', $merged);
$content = mb_convert_encoding($content, 'UTF-8', 'SJIS');
$content = str_replace("西暦,和暦,神武紀元,年の干支,日の干支", "", $content);
$content = "西暦,和暦,神武紀元,年の干支,日の干支\n" . $content;
// 結合したデータを保存
file_put_contents($saveDir . DIRECTORY_SEPARATOR . 'merged_calendar.csv', $content);
echo '完了' . PHP_EOL;
}
// メイン関数を実行(1800年から2024年までのカレンダーを取得し、./tmp/calendar/ディレクトリに保存)
main(2022, 2024, __DIR__ . '/tmp/calendar/');
