tubo28's blog
つぼの 140 文字より長い文章置き場
About me

西暦 200 年は閏年かは MySQL と Ruby で食い違う

Last Modified:
Tags: calendar

西暦 200 年が閏年かどうかを MySQL で評価すると No と答え、Ruby の Date は Yes と答える。

SELECT IF(DAY(LAST_DAY('200-02-01')) = 29, 'true', 'false') AS result;
=> false
irb(main):002> Date.parse("200-02-01").leap?
=> true

原因は、どちらの暦規則で過去を解釈するかの違いである。

グレゴリオ暦の閏年ルールはこうなっている。

200 年は以上のルールに従うと平年である。

一方、ユリウス暦の規則はシンプルに「4 で割り切れれば閏年」なので、200 年は閏年である。

さて、グレゴリオ暦は史実では 1582 年 10 月 15 日に始まりユリウス暦から切り変わった。したがってそれより過去をユリウス暦で見るか、グレゴリオ暦を過去に伸ばして見るかという選択がある。

前者を 先発グレゴリオ暦 と呼び、後者をユリウス暦とのハイブリッドなどと呼ぶことがある。MySQL は先発グレゴリオ暦、Ruby はハイブリッドを採用しているということである。

Claude に適当に調べさせると、だいたいの言語は先発グレゴリオ暦を採用しているか、どちらかの暦を明示的に指定できるようになっている。Ruby は例外でデフォルトでハイブリッドを採用しているようだ。理由は調べていない。

気をつけたいのが、日本史の年表は基本的にユリウス暦ベースだということである。国立天文台暦計算室も「統一的に日付を表現する場合、1582年10月4日まではユリウス暦、以降はグレゴリオ暦を用いるのが一般的です。」と明記している。

https://eco.mtk.nao.ac.jp/koyomi/wiki/A5B0A5ECA5B4A5EAA5AACEF1.html

年だけなら両者は一致することが多いので「大化元年 = 645 年」のような表記には差が出ないことが多い。旧暦の月日を西暦に換算する場面では数日ズレる。例えば大化の改新の詔の「大化 2 年正月元日」はユリウス暦で 646 年 1 月 22 日、先発グレゴリオで 646 年 1 月 25 日になる。年をまたぐタイミングであれば、さらにズレる可能性がある。例えば「大化 2 年 12 月 30 日」はユリウス暦で 646 年 1 月 20 日、先発グレゴリオで 646 年 1 月 23 日になる。

つまり、史料からとった日付をそのまま流し込むと、どのようにデータを読み込むかでずれることがあるということだ。あまりに難しい。現代のサービスを作るなら適当に 2000 年か 1900 年あたりを最小値にしてバリデーションをかけるのが無難である。注意。