カレンダー作成の基本的考え方
まずカレンダー計算の基本的な考え方は、起点となる日付からの経過日数を求め、それをもとに曜日を決定します。4、6、9、11月は30日、1、3、5、7、8、10、12月は31日ありますが、2月についてはうるう年だと29日、そうでない年は28日です。うるう年の判定は4で割り切れるが100では割り切れない年または400で割り切れる年がうるう年であり、それ以外の年はうるう年ではないと判定します。
紀元前46年からローマのユリウス・カエサルは新たな暦を制定しました。しかし彼の死後、人々が彼の指示の解釈を間違えて閏調整が3年おきに入れられてしまったためずれが生じていたのですが、アウグストゥスは紀元前9年から西暦8年の間は閏年を入れないことで調整し、ずれを元に戻しました。なお、その折にはセクスティーリスと呼ばれていた月が、アウグストゥスという名に変更されました。現在の8月の英語名Augustの由来です。
その後、1582年に教皇グレゴリウス13世が改暦を公布し、これが現在グレゴリウス暦として使用されています。その間にイレギュラーな閏年が入れられたりしたことはなかったのかどうかは、正確にはわかりません。となると、本ページの以下に記載したプログラムの計算方法によるカレンダー作成は、1582年以降に適用することにしておいたほうが安全かもしれません。
暦の歴史的なことは、次の本に詳しく記述されています。新書版で読みやすいと思います。
「暦と時間の歴史」Leofranc Holford-Strevens (著), 正宗 聡 (翻訳) (サイエンス・パレット)
この本を見ると、暦の改訂には農作業にとって重要となる春分の日を正しく決めることや、宗教的な問題も絡んでなかなか大変なのだなということがわかりますね。
MSのExcelにおいては、1900年1月1日を起点として日付が計算されますが、計算に誤りが発生する期間があります。このことの確認は、たとえばセルA1に1900/1/1という日付形式数値を入力しておき、セルA2に数式として =WEEKDAY(A1) と入力すると、1という値が表示されます。これは日曜日を表していますが、Windowsなどに付属のカレンダーソフトなどを用いて1900年1月を表示してみると1月1日は月曜日となっていることからも、Excelの曜日計算のずれがわかります。このずれは1900年1月および2月で発生します。手元のExcel2003ではそうなっていますが以後のバージョンではどうなのかは確認していません。
なお、1900年3月からは正常に曜日が計算されるのですが、その理由はExcelでは1900年2月をうるう年として計算していることによります。もちろん本来は1900年はうるう年ではないので、1900年1月からの曜日が1日ずれているという間違いを、うるう年でない1900年をうるう年として計算するという2番目の間違いにより、(たまたま?)打ち消す形となり、1900年3月以降は正常に曜日計算が行われているように思えます。
Java CUIによるカレンダー表示プログラム
コマンドプロンプト画面で、指定された年月の1か月分のカレンダーを表示します。カレンダーを作成するためには、紀元1年1月1日からの経過日数を求め、7で割った余りから曜日を判断します。
また、月の初日が何曜日になるかで、どの位置に1日目を表示するかを変える必要があります。1から9日までは一桁なので、スペースを一文字追加して位置合わせを行います。
実行すると、年と月を訊いてきます。入力しenterキーを押すとその月のカレンダーが表示されます。
プログラム内ではSystem.out.println()ではなく、System.out.print()を使っています。これは、後でGUI形式のプログラムを作る際、jTextArea.append()に簡単に移植できるようにするためですので、深い意味はありません。
import java.io.*; class CuiCalendar{ public static void main(String args[]) throws IOException{ //main()以外のメソッドも呼び出すので、自分自身の実体を作る。 CuiCalendar ccal = new CuiCalendar(); //makeTable()メソッドの呼び出し ccal.makeTable(); } public void makeTable() throws IOException { //指定された年月のカレンダー表示を行う。 //キーボードからデータを読み取るためのBufferedReaderインスタンス作成 int year, month, day=1, dayCount=1, n, LastDayOfMonth, i1; BufferedReader in=new BufferedReader(new InputStreamReader(System.in)); System.out.print("年?:"); year=Integer.parseInt(in.readLine()); System.out.print("月?:"); month=Integer.parseInt(in.readLine()); n = (getElapsedDay(year,month,1) ) % 7 ;//月初日の曜日番号を計算。日曜日が0。 //System.out.println()にすれば末尾の\nは不要。 System.out.print(" " + year+"年 "+month+"月"+"\n"); System.out.print(" Su Mo Tu We Th Fr Sa"+"\n"); //月末日の確定(28、29、30、31のどれになるか) if(month == 2){ //2月の場合 //うるう年かどうかをチェック if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0){ //うるう年 LastDayOfMonth=29; }else{ //うるう年ではない LastDayOfMonth=28; } }else{ //2月以外の場合 if(month == 4 || month == 6 || month == 9 || month == 11){ LastDayOfMonth=30; }else{ LastDayOfMonth=31; } } //カレンダーの表示 for(i1 = 0; i1<7; i1++){ //第1週の表示 if(i1>=n){ //nには、月初日の曜日番号が入っている System.out.print(" "); //表は1文字分右から表示する if(dayCount>9){ //日付が2桁の場合 System.out.print(Integer.toString(dayCount)); }else{ //日付が1桁の場合 System.out.print(" "+dayCount); //スペース1個+1桁 } dayCount++; }else{ //月初日がまだi1より後の曜日の場合は空白表示 System.out.print(" "); //スペース3個 } } System.out.print("\n"); //第2週以降の表示 while( dayCount <= LastDayOfMonth){ for(i1=0; i1<7 && dayCount<=LastDayOfMonth; i1++){ System.out.print(" "); //スペース1個右へ if(dayCount>9){//日付が2桁の場合 System.out.print(Integer.toString(dayCount)); }else{ //日付が1桁の場合 System.out.print(" "+dayCount); } dayCount++; } System.out.print("\n"); } } private int getElapsedDay(int yr, int mon, int dy ){ //パラメータで指定された年月日の、紀元1年1/1からの経過日数を計算 //(当日は1日にカウントする。つまり紀元1年1/1の経過日数は1となる。) int i1, year = yr, month = mon; int dayOfMonth = dy, elapsedDay = dayOfMonth; for(i1 = 1; i1 < year; i1++){ //うるう年かどうか判断しつつ、前年までの年数分の日数を加算。 if((i1 % 4== 0 && i1 % 100 != 0) || i1 % 400 == 0) elapsedDay += 366; else elapsedDay += 365; } for(i1=1;i1 < month; i1++){ //指定年の指定月の前月までの日数を加算。 if(i1==2){ //2月であればうるう年かどうか判断 if((i1 %4 == 0 && i1 % 100 != 0) || i1 % 400 == 0){ elapsedDay += 29; }else{ elapsedDay += 28; } }else{ //2月以外の場合 if(i1==4 || i1==6 || i1==9 || i1==11){ elapsedDay += 30; }else{ elapsedDay += 31; } } } return elapsedDay; } }
動作画面例
Kom., 2013