日付関連
参考になりそうなページ
閏年(leap year)
ユリウス暦(Julian calendar)は、紀元前46年、古代ローマで採用された。4年に1回、西暦年が4で割り切れる年を閏年としていた。
現行のグレゴリオ暦(Gregorian calendar)は1852年10月15日を金曜日として施行された。
グレゴリオ暦では、
コード 閏年判定
public static boolean isLeapYear(int y){ //if( y < 0 ) y = -y; //グレゴリオ暦のまま紀元前までさかのぼって考えるとき 1 BD-> y=0, 2 BD-> y=-1, ... ,n BD-> y=1-n return y % 4 == 0 && y % 100 != 0 || y % 400 == 0; }
Fairfieldの公式(Fairfield's congruence)
1年1月1日年から年月日までの日数は、
特に、 mod 7とすると、曜日が次のように求まる。
= 0 → Sun, 1 → Mon, 2 → Tue, 3 → Wed, 4 → Thu, 5 → Fri, 6 → Sat
ただし、求めたい日の月が1月、2月の場合はそれぞれ前年の13月、14月とする
( の場合は、とし、1年を、3月1日 〜 14月28日(閏年は29日)と仮定する)。
コード Fairfieldの公式
public static final int Fairfield(int y, int m, int d){ if (m < 3) { --y; m += 12; } return 365*y+y/4-y/100+y/400+306*(m+1)/10+d-428; }
証明 Fairfieldの公式
0年13月1日(1年1月1日)〜0月14月28日(1年2月28日)の日数は
31+28
1年3月1日 〜 年14月末日 の日数は ※閏年を考慮しなければ
0年13月1日〜年14月末日の閏年の回数は
を年3月1日〜年月末日までの日数とすると
年3月1日〜年月末日の日数は、
※年3月1日〜年月末日の日数の計算式は、式(1)以外にも考えられる。
年月1日〜年月日までの日数は
以上より、
1582年10月15日を金曜日としてグレゴリオ暦が施行されている。
mod 7 = 5となるので、
mod 7 = 0 → Sun, 1 → Mon, 2 → Tue, 3 → Wed, 4 → Thu, 5 → Fri, 6 → Sat
(証明終わり)
Zellerの公式(Zeller's congruence)
mod 7 (2)
= 0 → Sun, 1 → Mon, 2 → Tue, 3 → Wed, 4 → Thu, 5 → Fri, 6 → Sat
ただし、求めたい日の月が1月、2月の場合はそれぞれ前年の13月、14月とする
( の場合は、, とし、1年を、3月1日 〜 14月28日(閏年は29日)と仮定する)。
また、としたとき、
mod 7 (3)
とも表せる。
ただし、は整数。
コード Zellerの公式
enum WeekDay { Sun("Sunday"), Mon("Monday"), Tue("Tuesday"), Wed("Wednesday"), Thu("Thursday"), Fri("Friday"), Sat("Saturday"); private static final WeekDay[] Table = WeekDay.values(); public static final WeekDay getWeekDay(int i){ return Table[i]; } public final String description; private WeekDay(String description){ this.description = description; } @Override public String toString(){ return description; } } /** * Calculates a day of the week from (y,m,d) via Zeller's congruence. <br> * @param y year(>=-4800?) 1 BD-> y=0, 2 BD-> y=-1, ..., n BD-> y=1-n * @param m month * @param d day * @return a day of the week */ public static final WeekDay Zeller(int y, int m, int d){ y += 4800; //y<=0で使いたい場合 if (m < 3) { --y; m += 12; } return WeekDay.getWeekDay((y + y/4 - y/100 + y/400 + (13*m + 8)/5 + d) % 7); } /** * Calculates a day of the week from (100J+K, m, d) via Zelle's congruence. ※y > 0<br> * @param J year/100 * @param K year mod 100 * @param m month * @param d day * @return a day of the week */ public static final WeekDay Zeller(int J, int K, int m, int d){ if (m < 3) { int y=100*J+K-1; J=y/100; K=y%100; m += 12; } return WeekDay.getWeekDay((5*J+K+K/4+J/4+26*(m+1)/10 + d +6) % 7); }
証明 Zellerの公式
Fairfieldの公式より
mod 7
mod 7
mod 7
mod 7
mod 7
mod 7
が整数ならなので、
mod 7
mod 7 式(2)
ここでを代入すると、
mod 7
mod 7
mod 7
が整数ならなので、
mod 7
mod 7
mod 7
mod 7
より、
また、の小数部は、0.025, 0.5, 0.75のいずれかをとるので、の小数部は高々0.75+0.2475 = 0.9975<1
従って、
これらより、
mod 7 式(3)
(証明終わり)
ユリウス(通)日(Julian day)
ユリウス暦BC4713年1月1日の正午(世界時)を0日としたときの通算の日数。
またユリウス日から2400000.5を引いた数を修正(準)ユリウス日(modified Julian day)と呼ぶ。
ユリウス日⇔暦の求め方を探してみると、いろいろ見つかったけど、
負の数に対する除算、剰余演算結果によって計算結果が異なる恐れのなさそうな、
Julian day - Wikipedia, the free encyclopediaとSpaghetti Source - 日付関連を参考にさせてもらった(というか殆ど写しただけです)。
この辺の話って、天文学の本とかに乗ってんのかなぁ...
※紀元前の時代は、グレゴリオ暦ではありませんが、グレゴリオ暦施行からさかのぼって、計数した値になってます。
※紀元前n年を1-n年と表現しています(紀元前(後)0年は存在しない)。
ユリウス日⇔グレゴリオ暦
C++のBoost.GregorianとPHPのcal_from_jdでユリウス日 = 0 〜5373485日(10000/01/01)の範囲で確かめました。
※cal_from_jdでは紀元前n年を-nと表すが、下のプログラムは1-n年と表す。
Boost.Gregorianは1400/01/01〜10000/01/01の間で正常に動作するはずだが、
修正ユリウス日に関しては、値が負になる時、boost::gregorian::date::modjulian_day()(Boost. 1.47.0)は正しく求まらなかった。おそらくunsignedで計算しているため。ダメじゃん...。
※以降C++
/* transform hour, minute and sec into day */ inline double hms2day(int hour, int minute, int sec){ return (hour + (minute + sec/60.0)/60.0)/24.0; } /***************************************************** * Gregorian Calendar -> (modified) Julian day * 1 BD-> y=0, 2 BD-> y=-1, ..., n BD-> y=1-n * (Julian day + 1)%7 = 0->Sun, 1->Mon, .., 6->Sat * ((modified Julian day)%7 + 10 )% 7 = 0->Sun, 1->Mon, .., 6->Sat * ※負の数に対する除算や剰余の計算結果は、言語あるいは処理系によって異なるので注意 *****************************************************/ // http://www.prefield.com/algorithm/misc/date.html int greg2jd(int y, int m, int d, bool modflag = false) { //Ver. 1 y += 4800; if (m < 3){ --y; m += 12; } return 365*y + y/4 - y/100 + y/400 + (153*m - 457)/5 + d-32045 - (modflag ? 2400001 : 0); } double greg2jd_double(int y, int m, int d, bool modflag = false, int hour = 0, int minute = 0, int sec = 0){ return greg2jd(y, m, d, modflag) + hms2day(hour, minute, sec) - (modflag ? 0.0 : 0.5); } // http://en.wikipedia.org/wiki/Julian_day int gregtojd(int y, int m, int d, bool modflag = false){ // Ver. 2 int a = (14-m)/12; y = y + 4800 - a; m = m + 12*a -3; return d + (153*m+2)/5 + 365 * y + y/4 - y/100 + y/400 - 32045 - (modflag ? 2400001 : 0); } double gregtojd_double(int y, int m, int d, bool modflag = false, int hour = 0, int minute = 0, int sec = 0){ return gregtojd(y,m,d, modflag) + hms2day(hour, minute, sec) - (modflag ? 0.0 : 0.5); } /***************************************************** * Julian day -> Gregorian Calendar * 1 BD-> y=0, 2 BD-> y=-1, ..., n BD-> y=1-n *****************************************************/ // http://en.wikipedia.org/wiki/Julian_day void jd2greg(int jd, int& y, int& m, int& d){ int temp = jd + 32044; // +0.5 y = -4800; jd = temp/146097; temp %= 146097; y += (jd * 400); jd = (temp/36524 + 1)*3/4; temp -= (jd * 36524); y += (jd * 100); jd = temp/1461; temp %= 1461; y += (jd * 4); jd = (temp/365 + 1)*3/4; temp -= (jd * 365); y += jd; m = (temp*5 + 308)/153 -2; d = temp - (m + 4)*153/5 + 122; y += ((m + 2)/12 ); m = (m+2) % 12 + 1; d++; }
ユリウス暦⇒ユリウス日
こっちは確かめてません
/*************************************************** * Julian Calendar-> (modified) Julian day * 1 BD-> y=0, 2 BD-> y=-1, ..., n BD-> y=1-n ***************************************************/ //http://www.prefield.com/algorithm/misc/date.html long julian2jd(int y, int m, int d, bool modflag = false) { y += 4716; if (m < 3){ --y; m += 12; } return 365L*y + y/4 + (153*m - 457)/5 + d-1402 - (modflag ? 2400001 : 0); } double julian2jd_double(int y, int m, int d, bool modflag = false, int hour = 0, int minute = 0, int sec = 0){ return julian2jd(y, m, d, modflag) + hms2day(hour, minute, sec); }