いろいろな日付の計算(曜日・日付・日本の祝日)
Posted on 2012年3月27日(火) 02:01
カレンダーを作るにあたって必要な以下の点を考察してみます。
特に祝日の計算は日付クラスなんかを使っても出来ないことが多いので頑張りが必要です。
・日付→任意の曜日
・月が何日までか
・日本の祝日、国民の休日にあたるかどうか
まず任意の日付の曜日を得る方法。
これは簡単。有名なツェラーの公式を使います。
if (y*366+m*31+d < 1582*366+10*31+15) return -1; return (y + y/4 - y/100 + y/400 + (13*m+8)/5 + d) % 7;
次に任意の月の最後の日(=日数)を計算する方法。
2月以外は固定なので、要するに閏年の計算になります。
閏年を4年に1度と勘違いしている場合がありますが、それだけだと誤差が蓄積するので以下のルールになっています。
1.西暦が4で割り切れる年
2.ただし、100で割り切れる場合は例外
3.ただし、400で割り切れる場合は2のルールは適用しない
int last; switch(m){ case 1:last = 31;break; case 2: //うるう年の算出 if(y % 4 ==0){ if(y % 100 == 0) { if(y % 400 == 0){ last=29; }else{ last=28; } }else{ last=29; } }else{ last=28; } break; case 3:last = 31;break; case 4:last = 30;break; case 5:last = 31;break; case 6:last = 30;break; case 7:last = 31;break; case 8:last = 31;break; case 9:last = 30;break; case 10:last = 31;break; case 11:last = 30;break; case 12:last = 31;break; default: return 0; } return last;
最後に祝日・休日の計算方法について
今後制定される祝日については予測のしようがないので仕方が無いとして、既に制定されている祝日について考えます。
日付が固定されている祝日は楽ですが、ハッピーマンデーや振り替え休日がありますので注意が必要です。
また2007年の法律改正により、振替休日のルールが若干複雑になっています。
また、春分・秋分の日については法令としては前年になるまで確定しませんが、天文学的な春分日・秋分日と違う日になることはまず無いと推測できるので、計算により導いてみます。
以下は春分と秋分日の近似計算です。西暦1851年~2150年の間で利用できます。
//春分(3月) int GetVernalEquinoxDay() { int y = m_Systime.wYear; int d; if(y<1851) return -1; else if(y < 1900){ d = (int)(19.8277 + 0.242194 * (double)(y-1980) - ((y-1983)/4)); }else if(y < 1980){ d = (int)(20.8357 + 0.242194 * (double)(y-1980) - ((y-1983)/4)); }else if(y < 2100){ d = (int)(20.8431 + 0.242194 * (double)(y-1980) - ((y-1980)/4)); }else if(y < 2151){ d = (int)(21.8510 + 0.242194 * (double)(y-1980) - ((y-1980)/4)); }else return -1; return d; } //秋分(9月) int GetAutumnalEquinoxDay() { int y = m_Systime.wYear; int d; if(y<1851) return -1; else if(y < 1900){ d = (int)(22.2588 + 0.242194 * (double)(y-1980) - ((y-1983)/4)); }else if(y < 1980){ d = (int)(23.2588 + 0.242194 * (double)(y-1980) - ((y-1983)/4)); }else if(y < 2100){ d = (int)(23.2488 + 0.242194 * (double)(y-1980) - ((y-1980)/4)); }else if(y < 2151){ d = (int)(24.2488 + 0.242194 * (double)(y-1980) - ((y-1980)/4)); }else return -1; return d; }
最後に祝日です。 現在と2007年の改定前の祝日を拾うようにしてみました。 これに加えて、振替休日を考慮する必要があります。 2007より前は日曜日に重なった場合は月曜日を休日に、 2007年以降は「その日後においてその日に最も近い「国民の祝日」でない日」が休日です。
int c = (d-1) / 7 + 1; if(m==9) autumnalEquinoxDay =GetAutumnalEquinoxDay(); if(m==3) vernalEquinoxDay = GetVernalEquinoxDay(); if(m==1 && d==1){ return DAY_NEWYEARSDAY; }else if((y >= 1949 && y < 2000) && m==1 && d==15){ //1949-1999 1月、15日 return DAY_SEIJINNOHI; }else if((y >= 2000) && m==1 && w==1 && c == 2){ //2000~、1月、月曜日、第2 return DAY_SEIJINNOHI; }else if(y>= 1967 && m==2 && d==11){ //1967~、2月、11日 return DAY_KENKOKUKINENBI; }else if(y>= 1949 && y<1989 && m==4 && d==29){ //1949~1988、4月、29日 return DAY_TENNOUTANJOBI; }else if(y>=1989 && m==4 && d==29){ if(y>=2007) return DAY_SHOWANOHI; //1989~、4月、29日 return DAY_MIDORINOHI; }else if(y>=1949 && m==5 && d==3){ //1949~、5月、3日 return DAY_KENPOUKINENBI; }else if(y>=1986 && m==5 && d==4){ if(y>=2007) return DAY_MIDORINOHI; //1986~、5月、4日 return DAY_KOKUMINNOKYUJITU5; }else if(y>=1949 && m==5 && d==5){ //1949~、5月、5日 return DAY_KODOMONOHI; }else if(y>=1996 && y<2003 && m==7 && d==20){ //1949~2002、7月、20日 return DAY_UMINOHI; }else if(y>=2003 && m==7 && w==1 &&c==3){ //2003~、7月、月曜、第3 return DAY_UMINOHI; }else if(y>=1966 && y<2003 && m==9 && d==15){ //1966~2002、9月、15日 return DAY_KEIROUNOHI; }else if(y>=2003 && m==9 && w==1 &&c==3){ //2003~、9月、月曜、第3 return DAY_KEIROUNOHI; }else if(y>=1966 && y<2003 && m==10 && d==10){ //1966~2002、10月、10日 return DAY_TAIKUNOHI; }else if(y>=2003 && m==10 && w==1 &&c==2){ //2003~、10月、月曜、第2 return DAY_TAIKUNOHI; }else if(y>=1948 && m==11 && d==3){ //1948~、11月、3日 return DAY_BUNKANOHI; }else if(y>=1948 && m==11 && d==23){ //1948~、11月、23日 return DAY_KINROUKANSYANOHI; }else if(y>=1989 && m==12 && d==23){ //1989~、11月、23日 return DAY_TENNOUTANJOBI2; }else if(y>=2003 && m==9 && GetNumOfWeek(d-1)==3 && w-1 == 1 && (d+1)==m_AutumnalEquinoxDay){ //2003~、9月、前日が第3月曜日、翌日が秋分の日 return DAY_KOKUMINNOKYUJITU9; }else if(m==3 && d==m_VernalEquinoxDay){ //春分 return DAY_SYUNBUNNOHI; }else if(m==9 && d==autumnalEquinoxDay){ //秋分 return DAY_SYUUBUNNOHI; }