最初のページに戻ります。

総合の目次があるページに戻ります。

よく使うマニュアルです

Wiki

updated on 2004.06.23

14.3.曜日を求める

[ Previous ] [ HOME ] [ Upper ] [ Next ]


曜日計算は、いろいろな局面で必要となります。

バックアップや、マーケティング分析や、あるいはシステムプログラムの制御など。
月曜日は週の初めだからどうのこうのとか、昨年対比の曜日別対比(同日では曜日が違う)などの分析などに利用できます。

さて、やろうとしていることは、

あるサブルーチン(内部でも外部でも)に日付を渡すと、曜日コードが帰ってくればいいのです。

大きく分けて、方法は3つあります。

  1. 曜日計算する

  2. APIを使う

  3. カレンダーファイルをつくる


曜日計算の方法

2つの計算方法があります。

  1. 一つはZeller(ツェラー)の公式によるもの。
  2. 一つは、基準年を定めて、そこから今日までの年数と指定日までの日数を出して計算するもの。

自分は最初後者で作成していましたが、無駄な計算も多いので、前者を薦めます。式の中の [ ] は整数で切り捨てです。

Zellerの公式

追記 : 2000-12-31

間違われている方もいらっしゃるようなので、以下に気をつけてください。

1月、2月は、前年の13月、14月として計算しますので、たとえば

  • 2001年1月は、2000年13月
  • 2001年2月は、2000年14月

として計算します。

オリジナル

※このオリジナル法則の記事を見つけました[2000-3-8]

計算結果が 0=土曜,1=日曜, 2=月曜, 3=火曜, 4=水曜, 5=木曜, 6=金曜, となります。有効範囲は1583年から3999年まで。 ※下のZellerの公式の変形とは、曜日に対応する数字が違います。

yy1yy2年mm月dd日の曜日は、(yy1=西暦上2桁、yy2=西暦下二桁、1月,2月は前年の13月,14月とみなす。)

( yy2 + [ yy2 / 4] + [ yy1 / 4 ] + [ 26 ( mm + 1 ) / 10 ] - ( 2 * yy1 ) + dd ) mod 7

例 1997年11月24日

(97 + [ 97 / 4 ] + [ 19 / 4 ] + [ ( 26 * 12 ) / 10 ] - ( 2 * 19 ) + 24 ) mod 7
= (97+24+4+31-38+24) mod 7
=142  mod 7
= 2
= Monday

EXCELで確認してみましょう。 2000-3-1

zeller12.gif (47446 バイト)

A1 B1 C1 D1 E1
yy1 yy2 mm dd =MOD(B1+INT(B1/4)+INT(A1/4)+INT(26*(C1+1)/10)-(2*A1)+D1,7)

お詫び[2000-3-1]

注意:この計算式はRPG、CLPに適しません。下記の変形をご利用下さい。

掲示板で、指摘され、考えてみたら、整数の丸目の際、マイナスが有る場合に関して、はっきりと明確な答えが出ませんでした。-8.9を最も近い整数にまとめると言う場合、-9.0になる場合、-8.0になる場合、と2つ考えられます。実は、式の中にガウス記号[]があったので、気にとめていなかったのですが、RPGには、ガウス記号とか、「床」、「天井」の考え方がありませんでした。

小数以下を持つ数字を、整数にする場合、「床」(floor;xを越えない最大の整数)と「天井」(ceiling;xより小さくない最小の整数)があります。

floor(2.718)=2, floor(-2.718)=-3

ceil(2.718)=3, ceil(-2.718)=-2

RPGで「切り捨てる」場合、2.718=2となり、-2.718=-2になるので、正の整数では、floorで、負の整数では、ceilになります。そもそも切り捨てって、そういう物でしたね。(頭がごちゃごちゃしてきた...)

上記のEXCELシートでは、MOD(n,d) = n - d*INT(n/d)と考えていて、そのINT関数は、

INT(8.9) = 8
INT(-8.9) = -9

となっているので、floorを使っています。つまりMOD(-23,7)は、EXCELと、MVRを使ったRPGでは異なる結果になります。

EXCELでは、MOD(-23,7)は、-23-(7*int(-23/7))=-23-(7*-4)=-23+28=5です。

RPGでは、DIVとMVRで、

-23    DIV  7   CHK1
       MVR      CHK2

から、CHK1は-3で、CHK2は-2です。つまり、-23-(7*-3)=-23+21=-2になります。切り捨てをベースにしているからですね。

一応、「CHK2 < 0なら、CHK2=CHK2+7」で補正できます(元の数字の符号ではなく、計算結果の方)。これは、マイナスの数値の場合、RPGでは、-4ではなく-3のため-(-7)つまり+7を一回すれば、同値になるからです。また、プラスの数値の場合は、RPGもEXCELも同値になりますので、この補正はしません。

これでは、RPG(多分CLPも)で、簡単に値を求めるのは、無理でした。(2000年いっぱい)負数が出てしまうこの式よりも、この後で紹介する計算式の方がいいと思います。申し訳有りません。この方式での計算は止めてください。

これは、アルゴリズムだけでなく、それをRPGで如何に考えるのかまできちんと、フォローすべきでした。また、指摘していただきありがとうございました。

参考記事:16.35.MOD()とINT()にご用心

またこちらも、参考になります:FORTRAN CS1005: Homework 3 Finding Weekdays Given The Date負数なら+7でもいいようですね。[2000-3-8]

[2000-3-1]

Zellerの公式の変形

計算結果が 0=日曜, 1=月曜, 2=火曜, 3=水曜、4=木曜, 5=金曜, 6=土曜となります。有効範囲は1583年から3999年まで。 ※上のオリジナルとは、曜日に対応する数字が違います。

yyyy年mm月dd日の曜日は、(1月,2月は前年の13月,14月とみなす。)

( yyyy + [ yyyy / 4 ] - [ yyyy / 100 ] + [ yyyy / 400 ] + [ ( 2.6 * mm ) + 1.6 ] + dd ) mod 7

例 1997年11月24日

( 1997 + ( 1997 / 4 ) - ( 1997 / 100 ) + ( 1997 / 400 ) + [ ( 2.6 * 11 ) + 1.6 ] + 24 ) mod 7
= (1997+499-19+4+30+24) mod 7
= 2535 mod 7
= 1
= Monday

EXCELで確認してみましょう。 2000-3-1

zeller22.gif (48018 バイト)

A1 B1 C1 D1
yyyy mm dd =MOD(A1+INT(A1/4)-INT(A1/100)+INT(A1/400)+INT((2.6*B1)+1.6)+C1,7)

手計算によるもの

基本原理はそれほど難しくありません。知っていると、ちょっとした話題作りになるかも。

平年は1年52週間と1日である。

つまり、今年と来年が平年であれば、今日の曜日は、来年は1曜日ずれます。今日が月曜ならば、来年の今日は火曜日です。

基準年を決める(計算上の定数)

閏年直後の、平年の1月1日が月曜日である基準年(ここでは1917年とします)からの今日までの日数を割り出し、7で割った値が、0なら日曜日だし、1ならば月曜日だし....6ならば土曜日です。

つまり、例えば11月1日が土曜日ならば、11月7日(7+0)日は金曜日で、11月8日(7+1)は土曜日で、11月9日(7+2)は日曜日で...というように、

基準日から数えて、7+0は前日の曜日、7+1で同じ曜日、7+2で次の曜日...

となるわけです。視野をずうっと広げて、ある基準年からの日数を7で割った余りが曜日となります。

曜日は基準年からのずれで求まる

ところで、すべてが平年ならば、1年1曜日ずれるので、1月1日が月曜日ならば、12月31日は必ず同じ曜日になり、次の年の1月1日は火曜です。つまり、(7+1)となるので、1917年から前年までの年数そのものが曜日のずれとなります。なにも、全ての日を計算する必要はないのです。さらに、その間に何回閏年があるかで、その分一日ずれるのでそれも曜日のずれになります。そして、もし、今日の日付の曜日を求めるならば、今年の1月1日から今日までの日数も曜日のずれになります。

まとめると

yyyy年mm月dd日の曜日は、

( ( yyyy - 1917 ) + [ ( yyyy - 1917 ) / 4 ] + (yyyy/mm/ddのジュリアン日※) ) mod 7

で曜日コード(計算結果が 0=日曜日, 1=月曜日.... 6=土曜日)が求まります。

例 1997年11月24日

( (1997-1917) + [(1997-1917)/4] + 328 ) mod 7
=(80 + 20 + 328) mod 7
=(428) mod 7
=1
=Monday

※ジュリアン日(通算日)はcvtdatで取り出せますよね。

注意!!

上記の閏年の回数の計算では、2100年も閏年としてカウントしてしまいます。上記は2099年まで有効と考えてください。このページの第一版ではこの点に触れませんでした。もし、ローン計算で使う場合や、生命保険のように1契約が数十年にわたる場合は、Zeller公式にしてください。特に苦情がきたわけではないのですが、老婆心までに。

APIによるもの

ごめんなさいまだ試していません。リリアンデートなんて初めて知った。みなさん知ってました?
ウェブでありました。日付計算アルゴリズムを見てみて下さい。[2000-3-1]


カレンダーファイルをつくる

これは簡単ですね。日付をキーにしてフィールドにコードを、0から6までセット(1から7でも可)するだけですね。休日コードや月別の開始営業日や最終営業日など、どんどんセットすると便利でしょう。CLPならば、RTV形式のコマンドを作れば、いちいちCALLしないでも日付属性を取り出せますね。ただし、RPGでは、QCMDEXCではRTVできないので、そのコマンドのCPPかなにかをCALLします。カレンダーファイルは便利なので、曜日計算とあわせて、目的別に使っています。

参考リンク

Zeller's Algorithm

Homework 3

Calculate Day Of Week

 

[ Previous ] [ HOME ] [ Upper ] [ Next ]

You are at K's tips-n-kicks of AS/400

 

SEO [PR] 爆速!無料ブログ 無料ホームページ開設 無料ライブ放送