ユリウス暦やグレゴリオ暦の年月日からユリウス日(ユリウス通日)を求める、その逆のユリウス日から年月日を求めることを考える。
まずはそれぞれの暦の年月日からユリウス日を求めてみる。
ユリウス暦にしてもグレゴリオ暦にしても、1月と2月は前年の13月と14月ということで計算します。
また、紀元前1年は0年、紀元前2年以前は負数として計算する。
以下、JavaScriptの書き方で数式を書きますね。
ここでは、JDは整数として、時刻に使われる端数はないものとします。
ユリウス暦(jy年jm月jd日)→ユリウス日(JD)
if ( jm < 3 ) jy--, jm+=12;
JD = Math.floor(365.25*jy)+Math.floor(30.59*(jm-2))+jd+1721087;
グレゴリオ暦(gy年gm月gd日)→ユリウス日(JD)
if ( gm < 3 ) gy--, gm+=12;
JD = Math.floor(365.25*gy)+Math.floor(gy/400)-Math.floor(gy/100)+Math.floor(30.59*(gm-2))+gd+1721089;
式は長ったらしいが、それほど難しいことをやっているわけではない。
長ったらしく感じるのは、Math.floor関数を使って整数化しているからだろう。
まず、ユリウス暦の1721087や、グレゴリオ暦の1721089は、定数であるからユリウス日なんだろうということは解る。
1721087は、ユリウス暦の紀元前1年1月30日
1721089は、グレゴリオ歴の紀元前1年1月30日
であり、紀元前1年1月30日を起点にして、経過日数を立式しているということである。
次に気になるのが、共通して登場する Math.floor(30.59*(m-2)) の部分であろう。
月から2を引いたものに、30.59を掛け、整数化している。
m | 30.59*(m-2) |
3 | 30.59 |
4 | 61.18 |
5 | 91.77 |
6 | 122.36 |
7 | 152.95 |
8 | 183.54 |
9 | 214.13 |
10 | 244.72 |
11 | 275.31 |
12 | 305.90 |
13 | 336.49 |
14 | 367.08 |
Math.floorを外した整数化前の状態の値をみると、上手いこと経過日数を計算していることが解る。
30.58でも、30.60でも、上手く行かないので、絶妙な値であるとも言える。
あとは、閏年か平年かで2月29日分をどうするかって計算をしている。
つまり、ユリウス日からユリウス歴やグレゴリオ歴を求めるのは、それほど難しいというわけではない。
しかし、その逆算が難しいというか面倒なのである。
ネットには、様々なプログラムコードや式があるにはあるのだが、どこからこんな値が出てきたのが不明確で、その式を使う気になれない。
であれば、数学屋の私としては、何とかしたいと考えるのである。
では、どういう取っ掛かりで考えるかというと、ユリウス日の連続性であり、ユリウス暦やグレゴリオ暦の周期性である。
連続性、周期性とはどういうことなのか、手始めに西向く士を考えてみる。
ユリウス歴やグレゴリオ暦からユリウス日を求める際に、3月1日を起点として、平年であろうが閏年であろうが、2月という厄介者を一番最後に持ってくるようにする。
1年 | ||||||||||||||||
135×2+31=337日 | ||||||||||||||||
153日 | 153日 | 153日 | ||||||||||||||
月 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 1 | 2 | |||
日数 | ?? | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 | 31 | ?? |
西向く士も、一見すると不規則に並んでいるかのようですが、3月から開始し、5ヶ月毎に分けると、153日周期で、2月を除くと337日周期とみることができる。
しかし、ユリウス日からいきなり年、月を求めずして日を求めることは難しい。
一番簡単な年を求め、月、日の順番でいくのが妥当だろう。
ユリウス日(JD)→ユリウス暦(jy年jm月jd日)
1461日 | ||||||||||||||||||||||||||
306日 | 365日 | 365日 | 365日 | 29日 | ||||||||||||||||||||||
n年 | (n+1)年 | (n+2)年 | (n+3)年 | (n+4)年 | ||||||||||||||||||||||
月 | 02 | 03 | … | 12 | 01 | … | 02 | 03 | … | 12 | 01 | … | 02 | 03 | … | 12 | 01 | … | 02 | 03 | … | 12 | 01 | … | 02 | 02 |
日 | 29 | 01 | 31 | 01 | 28 | 01 | 31 | 01 | 28 | 01 | 31 | 01 | 28 | 01 | 31 | 01 | 28 | 29 | ||||||||
月' | 11 | 00 | 09 | 10 | 11 | 00 | 09 | 10 | 11 | 00 | 09 | 10 | 11 | 00 | 09 | 10 | 11 | 11 | ||||||||
n'年 | (n'+1)年 | (n'+2)年 | (n'+3)年 | |||||||||||||||||||||||
365日 | 365日 | 365日 | 365日 |
この表をみれば解ると思うが、4年に1回の閏年で、3月1日を起点とし、2月29日を終点とする。
この4年が1461日周期で、365日周期が4回と2月29日の1日分である。
では、起点とする3月1日を何年にするか。
紀元前4713年3月1日、JD=60としてみる。
jy = Math.floor((JD-60+0.5)/1461*4);
で適当なものを入れて確認してみると、求めたいjyと4712の差がありました。
jyが負になると、日の計算で厄介になるかもしれないので、とりあえずこのままにしておく。
続いて、月を考える。
m0 = (JD-60)%1461;
これが起点からの経過日数であり、m0=1460が2月29日になります。
これを更に365日で剰余を取り、年ごとの3月1日からの経過日数を求める。
m1 = m0%365;
これで、m1=0が、3月1日、最後は2月29日になります。
最後の2月29日に365を加えたいので、
m2 = 365*Math.floor((m0+1)/1461);
これらから、3月1日を起点とした経過日数は、
m3 = m1+m2;
となる。
これをユリウス日計算に使われる30.59で割って、月を求める。
m4 = Math.floor((m3+0.4)/30.59);
下駄を0.4としましたが、0.36から0.48の間ならよい。
jm = m4+3;
解説のため、数式を分けましたが、実際はここまで分ける必要はありません。
年、月が求まったので、これを使えば、JDとの差分から日が求まる。
jd = JD-Math.floor(jy*365.25)-Math.floor(30.59*(jm-2))-29;
最後に人前に出せる数値にするため、
jy -= 4712;
if ( jm>12 ) jy++, jm-=12;
紀元前を表示するするときは、-jy+1で問題なしでしょう。
ユリウス日(JD)→グレゴリオ暦(gy年gm月gd日)
ユリウス歴の1461日周期を25周と-1日で100年分、それを4周と1日で400年分である。
36524×4+1=146097日 | |||||||||
100×4+1=400年 | |||||||||
1461×25-1=36524日 | … | 36524日 | 02 月 29 日 | ||||||
4×25-1=100年 | 100年 | ||||||||
1461日 | … | 1461日 | 1460日 | 1461日 | … | 1461日 | 1460日 | ||
4年 | 4年 | 4年-1 | 4年 | 4年 | 4年-1 |
では、グレゴリオ暦の起点とする3月1日を何年にするか。
紀元前4401年3月1日、JD=114053としてみる。
まずは、年と月を考えます。
a = (JD-114053)%146097;
b = a%36524;
c = b%1461;
y0 = 400*Math.floor((JD-114053+0.5)/146097);
y1 = 100*Math.floor(a/36524)-Math.floor((a+1)/146097);
y2 = 4*Math.floor(b/1461);
y3 = Math.floor(c/365)-Math.floor((c+1)/1461);
gy = y0+y1+y2+y3;
この状態で、gyは4400の下駄を履いているが、400年周期であるから、月や日の計算には支障がないというか、下駄を履かせたままでないと、日に支障が出る。
m0 = c%365+365*(Math.floor((a+1)/146097)+Math.floor((c+1)/1461));
これがすべての年の3月1日からの経過日数。
gm = Math.floor((m0+0.4)/30.59)+3;
gd = JD-(Math.floor(365.25*gy)+Math.floor(gy/400)-Math.floor(gy/100)+Math.floor(30.59*(gm-2)))-114053+31
最後に、
gy -= 4400;
if ( gm>12 ) gy++, gm-=12;
こんな感じで、プログラムを組んでみるかな。