計算方法を理解しよう
まず年と月と日から曜日が決定できるかを考えてみよう。
2025年3月12日は何曜日か? と聞かれてもとっさに答えられる人は少ないだろう。では計算で求めることができるだろうか?
各月が何日かは、あらかじめ決まっている。だからたとえば今月(2005年11月)が30日で終わることはあらかじめわかっているし、11月30日が水曜であれば12月1日が木曜であることもあらかじめわかっている。だからどんな先の日付であっても、その日のが何曜日であるかはあらかじめ決まっている。
このように、あらかじめ一つに定まっていることを、工学的な言葉でいえば「決定的=deterministic」であるという。反対に天気のようにあらかじめ予測がつかないことを「非決定的=undeterministic」と言う。曜日の計算は決定的な問題だ。
次に、曜日を求める方法を考えてみる。曜日は7日間で一巡し、以後7日間毎に同じ順序でくりかえす。今日からx日後の曜日を考えると以下のようになる。
今日 | 明日 | 明後日 | ... | ||||||||||||||
今日からの日数 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
曜日 | 金 | 土 | 日 | 月 | 火 | 水 | 木 | 金 | 土 | 日 | 月 | 火 | 水 | 木 | 金 | 土 | 日 |
いいかえれば、指定された日が今日から何日後かがわかれば、あとは7日毎に曜日がくりかえすことを利用すれば曜日はすぐにわかることになる。
具体的には、今日から N 日目の曜日を求めるには、まずNが何週目かを求める。
W = N \ 7
そして、その週の最初の日を再度計算してNから引く。
X = N - 7 * W
これで7日間周期のW回目のX日目であるということが計算できる。
X だけを一発で求める方法もある。それは「割り算のあまり」を計算する方法だ。以前習ったように、Mod という単語は「演算子」として使うことができて、割り算のあまりを計算する。
X = N Mod 7
という計算を行えば、X(Nを7で割ったあまり)が一発で計算できる。
さて、N(今日から与えられた日までの日数)がわかれば曜日が計算できることはわかったが、問題はNをどうやって計算するかである。
話を簡単にするために、今日からの日数Nのかわりに2000年のお正月からの日数Lを求めることにする。
L = 2000年のお正月からの日数。
このような問題を考える場合、まず簡単な場合を考える。たとえば本当は一ヶ月の長さは 28, 29, 30, 31日のどれかだが、簡単にするために1ヶ月は31日で固定で一年は365日だったとしよう。そうすれば、
Q = (Y - 2000) * 365 + (M -1) * 31 + D
という計算式でよいだろう。
あいにく月の長さは月毎にことなる。以下のようになっている。
月 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ||
長さ | 普通の年 | 31 | 28 | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 | |
うるう年 | 31 | 29 | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 |
ではQとLはどの程度異なるだろうか? そのためには各月の長さが30とどれだけ違うかをかんがえればよい。まず普通の年について考えてみる。
月 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
Qの計算に 使った日数 |
31 | 31 | 31 | 31 | 31 | 31 | 31 | 31 | 31 | 31 | 31 | 31 |
実際の日数 | 31 | 28 | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 |
誤差 | 0 | -3 | 0 | -1 | 0 | -1 | 0 | 0 | -1 | 0 | -1 | 0 |
z = その月の Qからのずれ |
0 | 0 | -3 | -3 | -4 | -4 | -5 | -5 | -5 | -6 | -6 | -7 |
このように月によって+1または-2すればよいことがわかる。
簡単な方法としては、こんな風に書いてもよい。(チェックしてないのでもしかしていると間違っているかもしれないが)
if M = 3 Or M = 4Then L = Q - 3 elseif M = 5 Or M = 6 Then L = Q - 4 elseif M = 7 Or M = 8 Or M = 9 Then L = Q - 5 elseif M = 10 Or M = 11 Then L = Q - 6 elseif M = 12 Then L = Q - 7 end if |
でもこれでは面白くないので、もうちょっとコンパクトに書く方法を工夫しよう。
月と差の累積zの関係をみると, 7-8月をのぞけばだいたい2ヶ月おきに-1されている。
ためしに - m \ 2 と比較するとこんな感じ
m | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
z | 0 | 0 | -3 | -3 | -4 | -4 | -5 | -5 | -5 | -6 | -6 | -7 |
-m \ 2 | 0 | -1 | -1 | -2 | -2 | -3 | -3 | -4 | -4 | -5 | -5 | -6 |
まず-1すれば8月-12月は合いそうだ
。
m | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
z | 0 | 0 | -3 | -3 | -4 | -4 | -5 | -5 | -5 | -6 | -6 | -7 |
-m \ 2 - 1 | -1 | -2 | -2 | -3 | -3 | -4 | -4 | -5 | -5 | -6 | -6 | -7 |
3月から7月までをあわせるには7月まではzを左へ1つずらせばよい。そのためにはm < 7の場合にmのかわりにm+1を使えばよい。
m | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
z | 0 | 0 | -3 | -3 | -4 | -4 | -5 | -5 | -5 | -6 | -6 | -7 |
-(m + 1 - m \ 8) \ 2 - 1 | -2 | -2 | -3 | -3 | -4 | -4 | -5 | -5 | -5 | -6 | -6 | -7 |
あとはm <= 2の場合に+2するだけだ。これは( 2 - (m + 7) \ 10 * 2を加えればよい。
m | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
z | 0 | 0 | -3 | -3 | -4 | -4 | -5 | -5 | -5 | -6 | -6 | -7 |
-(m + 1 - m \ 8) \ 2 - 1 + 2 - ((m + 7) \ 10) * 2 | 0 | 0 | -3 | -3 | -4 | -4 | -5 | -5 | -5 | -6 | -6 | -7 |
うるう年についても同様に考えてみよう。
うるう年自体はy Mod 4 = 0 の場合で y Mod 100 が0でない場合に発生する
したがって、
y \ 4 - y \ 100 + y \ 400
を加算すれば、 3月以後については正しい日付が計算できる。
1~2月については、前年度までのうるう年の補正値のままであるからyのかわりにy - 1を使えばよい。したがって、
v = y - 1 + (m + 7) \ 10
u = (v \ 4 - v \ 100 + v \ 400) Mod 7
を使えは1~12月について正しいうるう年補正ができることになる。
以上基準となるqに、月の補正x, うるう年補正 u を合計し、日曜日が0になるようにし、2000年1月1日が土曜(5)になるようにするならば、
w = (q + u + z + 5) Mod 7
という計算をすればよい。
以上をまとめると
Dim y As Integer '年 : 西暦 Dim m As Integer '月 : 1~12の数 Dim d As Integer '日 : 1~31の数 Dim w As Integer '曜日 : 0 = 日 ' 1 = 月 ' 2 = 火 ' 3 = 水 ' 4 = 木 ' 5 = 金 ' 6 = 土 Dim v, u, q, z y = Val(TextBox1.Text) m = Val(TextBox2.Text) d = Val(TextBox3.Text) v = (y - 1 + (m + 7) \ 10) u = (v \ 4 - v \ 100 + v \ 400) Mod 7 q = ((y - 2000) * 365 + m * 31 - 32 + d) Mod 7 z = -(m + 1 - m \ 8) \ 2 - 1 + 2 - ((m + 7) \ 10) * 2 w = (q + u + z + 5) Mod 7 Label5.Text = Mid$("日月火水木金土", w + 1, 1) |
曜日計算については様々なwebページもあるので、検索サイト等でも調べてみよう。
曜日 計算式 |