UWSCで1949年以降の祝日を算出する
現在の祝日は、以下がシンプルでおすすめ。
UWSCで祝日を算出する - じゅんじゅんのきまぐれ
過去も気になる人向けに考えてみた。
Googleカレンダー系も結構人手メンテが必要なようなので、算出できるのは有効かも。
こっちの今後のメンテは、私の気分次第。
1949年以降なら大体あっていると思われる。(なんとなくはあっている。適当)
追記 2021/10/12
2021年東京オリンピック周りの変更を追加。
追記 2018/06/25
2020年東京オリンピック周りの変更を追加。
機能しなくなったオンライン系をばっさり削除。
追記 2014/05/27
「山の日」追加
スクリプト
Holiday.uws
OPTION EXPLICIT Holiday.CheckUpdated(PARAM_STR) IFB LENGTH(PARAM_STR) > 1 THEN DIM holiday FOR holiday in Holiday.GetYear(PARAM_STR[1]) PRINT holiday[0] + " " + holiday[1] NEXT ELSEIF GET_UWSC_NAME = "Holiday.uws" THEN DIM ret = 0, i, holidays, f, hs holidays = INPUT("holidays.ymlをDrag&Dropしてください") IFB LENGTH(holidays) = 0 THEN ret = ret + 1 ELSE f = FOPEN(holidays) IFB f = -1 THEN ret = ret + 1 ELSE holidays = SPLIT(FGET(f, F_ALLTEXT), "<#CR>") FCLOSE(f) ENDIF ENDIF IFB ret = 0 AND LENGTH(holidays) > 0 THEN FOR i = 1 TO LENGTH(holidays) - 1 // 1行目はSkip holidays[i-1] = SPLIT(holidays[i], ": ") NEXT GETTIME(1, holidays[LENGTH(holidays)-2][0]) hs = Holiday.Get(holidays[0][0], Holiday.MakeDate(G_TIME_YY, G_TIME_MM, G_TIME_DD), 5000) ENDIF f = LENGTH(holidays) - 1 IFB ret = 0 AND f > 0 THEN IF LENGTH(hs) < f THEN f = LENGTH(hs) FOR i = 0 TO f - 1 IFB hs[i][0] <> holidays[i][0] OR POS(hs[i][1], holidays[i][1]) = 0 THEN PRINT "不一致 Module:" + hs[i][0] + hs[i][1] + " HardCode:" + holidays[i][0] + holidays[i][1] ret = ret + 1 ENDIF NEXT IFB LENGTH(holidays) <= LENGTH(hs) THEN FOR i = f TO LENGTH(hs) - 1 PRINT "不一致 Module:" + hs[i][0] + hs[i][1] + " HardCode:なし" ret = ret + 1 NEXT ELSE FOR i = f TO LENGTH(holidays) - 2 PRINT "不一致 Module:なし HardCode:" + holidays[i][0] + holidays[i][1] ret = ret + 1 NEXT ENDIF ENDIF IFB ret > 0 THEN MSGBOX("試験NG:" + ret) ELSE MSGBOX("試験OK") ENDIF ENDIF MODULE Holiday FUNCTION CheckUpdated(path[], bPrint=TRUE) GETTIME() DIM p = "Holiday.uws", sc = CREATEOLEOBJ("Scripting.FileSystemObject"), y = G_TIME_YY, ms, i, dlm IF LENGTH(path) THEN p = path[0] RESULT = sc.FileExists(p) IFB RESULT THEN dlm = sc.GetFile(p).DateLastModified WITH CREATEOLEOBJ("VBScript.RegExp") .Pattern = "(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+)" ms = .Execute(sc.GetFile(p).DateLastModified) IFB ms.Count > 0 THEN dlm = "" FOR i = 0 TO ms.Item(0).SubMatches.Count - 1 IF LENGTH(ms.Item(0).SubMatches(i)) < 2 THEN dlm = dlm + "0" dlm = dlm + ms.Item(0).SubMatches(i) NEXT ENDIF ENDWITH GETTIME(0, dlm) RESULT = (G_TIME_YY >= y) IF !RESULT AND bPrint THEN PRINT "モジュールが古いかもしれません。祝日判定条件の見直しをお願いします" ELSE IF bPrint THEN PRINT "更新チェックができません。CALL文を修正し第一引数にパスを指定してください" ENDIF FEND FUNCTION MakeDate(y, m, d) IF VAL(y) < 100 THEN y = y + 2000 IF VAL(m) < 10 THEN m = "0" + m IF VAL(d) < 10 THEN d = "0" + d RESULT = y + "-" + m + "-" + d FEND FUNCTION GetNthDay(n, w, b=EMPTY) IF b <> EMPTY THEN GETTIME(0, b) DIM d = 1 - G_TIME_DD GETTIME(d, b) // 月初日の曜日取得 w = w - G_TIME_WW d = d + w + ((w < 0) + n - 1) * 7 // 第何分だけずらす GETTIME(d, b) RESULT = G_TIME_YY4 + "-" + G_TIME_MM2 + "-" + G_TIME_DD2 FEND FUNCTION GetYear(y=EMPTY) // 注意! // 1948年以前と2100年以降は未対応。2014年以降は修正が必要かも IFB y = EMPTY THEN GETTIME() y = G_TIME_YY ENDIF IFB y < 1949 OR y > 2099 THEN RESULT = SAFEARRAY(0, -1) EXIT ENDIF HASHTBL tbl = HASH_SORT // 特別組 IF y = 1959 THEN tbl[y+"-04-10"] = "皇太子明仁親王の結婚の儀" IF y = 1989 THEN tbl[y+"-02-24"] = "大喪の礼" // 昭和天皇の大喪の礼 IF y = 1990 THEN tbl[y+"-11-12"] = "即位礼正殿の儀" IF y = 1993 THEN tbl[y+"-06-09"] = "結婚の儀" // 皇太子徳仁親王の結婚の儀 IFB y = 2019 THEN tbl[y+"-05-01"] = "休日" // 令和天皇の即位 tbl[y+"-10-22"] = "休日" // 令和天皇の即位礼正殿の儀 ENDIF // 固定組 tbl[y+"-01-01"] = "元日" tbl[y+"-05-03"] = "憲法記念日" // 1948年から tbl[y+"-05-05"] = "こどもの日" tbl[y+"-11-03"] = "文化の日" // 明治節 tbl[y+"-11-23"] = "勤労感謝の日" // 新嘗祭。1873年からか?。勤労感謝の日としては1948年から tbl[y+"-04-29"] = "天皇誕生日" // 昭和。年が進むと名称が変わる IF y >= 1967 THEN tbl[y+"-02-11"] = "建国記念の日" // 紀元節 IFB y >= 2007 THEN tbl[y+"-04-29"] = "昭和の日" tbl[y+"-05-04"] = "みどりの日" IFB y >= 2020 THEN tbl[y+"-02-23"] = "天皇誕生日" // 令和 ELSEIF y < 2019 THEN tbl[y+"-12-23"] = "天皇誕生日" // 平成 ENDIF ELSEIF y >= 1989 THEN tbl[y+"-04-29"] = "みどりの日" tbl[y+"-12-23"] = "天皇誕生日" // 平成 ENDIF IF y >= 2016 AND y <> 2020 AND y <> 2021 THEN tbl[y+"-08-11"] = "山の日" // 第n月曜組 IF y >= 2000 THEN tbl[GetNthDay(2, 1, y + "-01-15")] = "成人の日" IFB y >= 2003 THEN tbl[GetNthDay(3, 1, y + "-09-15")] = "敬老の日" IFB y = 2020 THEN tbl[y+"-07-23"] = "海の日" tbl[y+"-07-24"] = "スポーツの日" tbl[y+"-08-10"] = "山の日" ELSEIF y = 2021 THEN tbl[y+"-07-22"] = "海の日" tbl[y+"-07-23"] = "スポーツの日" tbl[y+"-08-08"] = "山の日" ELSE tbl[GetNthDay(3, 1, y + "-07-20")] = "海の日" IF y > 2020 THEN tbl[GetNthDay(2, 1, y + "-10-10")] = "スポーツの日" ELSE tbl[GetNthDay(2, 1, y + "-10-10")] = "体育の日" ENDIF ENDIF ELSE tbl[y+"-07-20"] = "海の日" tbl[y+"-09-15"] = "敬老の日" tbl[GetNthDay(2, 1, y + "-10-10")] = "体育の日" ENDIF ELSE IF y >= 1949 THEN tbl[y+"-01-15"] = "成人の日" IFB y >= 1966 THEN tbl[y+"-09-15"] = "敬老の日" tbl[y+"-10-10"] = "体育の日" IF y >= 1996 THEN tbl[y+"-07-20"] = "海の日" ENDIF ENDIF // 春分の日・秋分の日(1918-2099用) 1948年の秋分の日が最初の祝日 tbl[y+"-03-"+INT(22-(3-((y-1892) MOD 4)+INT((y-1892)/33))*0.25)] = "春分の日" tbl[y+"-09-"+INT(25-(3-((y-1820) MOD 4)+INT((y-1820)/32))*0.25)] = "秋分の日" // 振替休日 1973年から(ただし、1973-02-11は除く) DIM i = 0, b IFB y >= 1973 THEN WHILE i < LENGTH(tbl) IFB GETTIME(1, tbl[i, HASH_KEY]) > -848361600 THEN b = G_TIME_YY4 + "-" + G_TIME_MM2 + "-" + G_TIME_DD2 IFB G_TIME_WW = 1 THEN WHILE tbl[b, HASH_EXISTS] GETTIME(1, b) b = G_TIME_YY4 + "-" + G_TIME_MM2 + "-" + G_TIME_DD2 WEND tbl[b] = "振替休日" i = i + 1 ENDIF ENDIF i = i + 1 WEND ENDIF // 国民の休日 1986年から IFB y > 1985 THEN i = 1 WHILE i < LENGTH(tbl) IFB GETTIME(1, tbl[i-1, HASH_KEY]) = GETTIME(-1, tbl[i, HASH_KEY]) AND G_TIME_WW <> 0 THEN tbl[G_TIME_YY4+"-"+G_TIME_MM2+"-"+G_TIME_DD2] = "休日" // 国民の休日 i = i + 1 ENDIF i = i + 1 WEND ENDIF // 結果格納 RESULT = SAFEARRAY(0, LENGTH(tbl) - 1) FOR i = 0 TO LENGTH(tbl) - 1 RESULT[i] = SAFEARRAY(0, 1) RESULT[i][0] = tbl[i, HASH_KEY] RESULT[i][1] = tbl[i, HASH_VAL] NEXT FEND FUNCTION Get(sd=EMPTY, ed=EMPTY, num=50) DIM sdt = GETTIME(0, sd), sdy = G_TIME_YY DIM edt = GETTIME(0, ed), edy = G_TIME_YY DIM res = SAFEARRAY(0, edy - sdy), i, si, ei, ni, j, k, r FOR i = sdy TO edy res[i-sdy] = GetYear(i) NEXT sdy = edy - sdy FOR i = 0 TO sdy r = res[i] SELECT i CASE 0 // 開始位置検索 si = 0 WHILE (GETTIME(0, r[si][0]) < sdt) si = si + 1 IF si >= LENGTH(r) THEN BREAK WEND ni = LENGTH(r) - si CASE sdy // 終了位置検索 ei = 0 WHILE (GETTIME(0, r[ei][0]) < edt) ei = ei + 1 IF ei >= LENGTH(r) THEN BREAK WEND ni = ni + ei DEFAULT ni = ni + LENGTH(r) SELEND NEXT // 結果の再格納 RESULT = SAFEARRAY(0, ni - 1) k = 0 FOR i = 0 TO sdy r = res[i] edy = LENGTH(r) - 1 IF i = sdy THEN edy = ei - 1 FOR j = si TO edy RESULT[k] = SAFEARRAY(0, 1) RESULT[k][0] = r[j][0] RESULT[k][1] = r[j][1] k = k + 1 IF k >= num THEN BREAK 2 NEXT si = 0 NEXT FEND ENDMODULE
算出という意味で必要なのは、Get/GetYear/GetNthDay関数。
他は、テストコードだったり。
ただし、何気に重要なのは、CheckUpdated関数。
スクリプト冒頭に
Holiday.CheckUpdated(PARAM_STR)
と書いてあり、ファイルの更新日付の年が現在年より古い場合、警告をPrintします。
地に書いてあるので、CALLでも警告します。
毎年一回、チェックを促すために存在します。
チェックのため、違うディレクトリーにあるHolidayモジュールをCallする場合、第一引数にパスを指定してください。(同じ場合は省略可)
CALL ..\lib\Holiday("..\lib\Holiday.uws")
DRY信者的には気持ち悪いですが、他に方法を思いつきませんでした。
1948年の祝日の施行は中途半端なので、1949年からとしました。
その年限りの祝日を特別組として追加。(忘れてた)
春分の日・秋分の日は、Wikipediaの判定文を数式に直して一部の誤差を断念して1918年からに対応した。
本関数の対象が1949年からなので充分かと。
いずれにしても、、、間違う可能性は否定できない(今のところは正しいけどね!)
テストコードに書いたのは、ハードコードしている人のymlファイルを利用。
https://github.com/holiday-jp/holiday_jp/blob/master/holidays.yml