西暦の年月日から曜日を算出する計算方法は1,2月を前年の13,14月にするツェラーの公式として広く知られています。C言語でコンパクトな曜日算出関数を作ってみました。
ガウス関数とmod関数を使っています。C言語では演算に使う変数を整数型にすればガウス関数を使うこともなく、またモジュロ演算子(%)を使えばmod関数も必要ありません。
一方、暦に目を移すと現在、利用されている暦は1582年に制定されたグレゴリオ暦です。わが国では明治5年から使われています。
平年を365日とし、400年間に97回の閏年をもうけ、1年の平均日数を365.2425日としています。日付から曜日を割り出すテーマはコンピュータにうってつけです。
今ではC,EXCELなどの言語処理系が曜日を算出する関数やマクロ定義を用意していますが、計算式を抑えておくと何かと便利です。以下のような作成基準で計算式を検討してみます。
①if文などの判別式を使わない。閏年かの判定や月の大小なども計算式の中で吸収する。
②簡潔なプログラムコードとする。
③他の言語に移行可能とする。 ④C言語でプログラミング。
⑤検証コードも示す。
※日付から曜日を算出する関数
int date2week( int y, // 西暦年,1月、2月でもそのまま指定
int m, // 月,1月、2月で13,14に修正はしない
int d) // 日
{
int l; // work
l = y + (m - 8) / 6; // この処理と(m+9)%12*26)/10で1,2月を前年の13,14月扱いに
l = ((5 + (m + 9) % 12 * 26) / 10 + (l * 5) / 4 - l / 100 + l / 400 + d) % 7;
// ここで月別の大小を考慮に入れたずれ数を求む
l = (l + 2) % 7; // 火曜始まりを日曜始まりにする。
return l; // 日(0)~土(6)。
}
上記コードはコンピュータサイエンス誌bit 1976-7,FORTRAN演習問題解答を参考にしています。30年以上も前のコードですが、いまだに光り輝いています。その記事では語られていませんが、原本はツェラーの公式と思われます。
※検証コード
#if 1 // test-1
for(i=1990; i<2101; ++i){
j = date2week(i, 2, 28);
w = date2week(i, 3, 1);
c = w - j;
if((c==1)||(c==-6)) c=' '; // 平年
else c='@'; // うるう年
uPrint("i=%d %d %d %c\n", i, j, w, c);
}
#else // test-2
n=0;
i=2099;
w = date2week(i, 12, 31);
uPrint("Y=%d w=%d\n", i, w);
for(++i; i<=2100; ++i){
mmax[1]=28; // 平年
if((i % 4)==0){
if((i%100)==0){
if((i%400)!=0) goto heinen; // 平年扱い
}
mmax[1]=29; // うるう年
}
heinen:
for(k=0; k<12; ++k){
for(l=1; l<=mmax[k]; ++l){
j = date2week(i, k+1, l);
c = j - w;
uPrint("Y=%d M=%d D=%d j=%d %d\n", i, k+1, l, j, c);
if((c!=1)&&(c!=-6)) ++n; // 内部エラー
w = j;
}
}
}
uPrint("内部エラー検出回数=%d\n", n);
#endif
※暦のルールに寄せての雑感
すでに一昔は過ぎてしまったが、2000年問題でどうしてあのような大騒ぎをしたのかと不思議です。閏年の規定は西暦年を4で割り切れれば閏年。その他は平年との決まりだが、100で割り切れる場合は400で割り切れる年だけを閏年とする約束です。
この規定からすると2000年は閏年になり、結果として1996年や2004年の閏年と同じだったのです。直近の例外は2100年だから騒ぐのは100年早かったわけです。
そのほか、明治政府は旧暦の明治5年(1872)12月3日を、新暦の明治6年1月1日としました。明治5年の12月はたった3日間であり、12月分の給料を削減するために強行したという説もあります。
2000年が20世紀最後の年で21世紀は2001年からというのもピンときません。2000年1月1日の年賀状に「旧世紀はたいへんお世話になりました。今世紀もよろしくお願いします」とあったそうです。気分では2000年が21世紀の始まりです。