日本国民として知っておくべきこと:国民祝日

まずは技術的な話

Windows10のカレンダーアプリには、OutlookGmailの国民祝日があります。普通に考えてOutlookAPIで簡単に取れるだろうと思って、いろいろ調べてみたんですが、サンプル通りにやってもうまくいきません。

 

休日一覧をウェブから取ってくればいっか 

年に一度のイベントと思って手入力してもいいんですが、やっぱりそれではダサいのでもうちょっと調べてみたらGoogleカレンダーAPIで可能だということが分かりました。そしてこのAPIを使って自前でGithubを自動更新しているページを見つけたので、ここから休日を取ってくるコードを書いてみました。商用・非商用OKということなので問題ないと思います。

 

すべてはこれが始まりでした

もうひとつCalendar Service (http://calendar-service.net/api.php) というページも見つけましたが、この2つのページで日付の差異があることに気が付きました。普段は国民祝日なんて気にも留めていないし、日本は祝日が多すぎてよくないと思っているくらいなので、ましてやそれぞれの休みの意味とか背景などどうでもよく、休みなら仕方なく休むかという感じでした。

 

違いが発生した理由その1 天皇誕生日

2019年12月23日は天皇誕生日のはずでした。しかし、皇位継承元号も変わり、この日は平日に変更されました。そして、ヤフーファイナンスのトップページもこの有様です。ヤフーは12/23 13:20くらいまで株価を更新していませんでした。おそらく2階建てのネタになるでしょう。

f:id:gungnir46:20191223132011p:plain

しかしながら、わたしが勘違いした理由にはもう一つあって、それはOutlookの休日カレンダーです。左ペインにあるとおり、OutlookGmailの両方に日本の休日というイベントがあり、12/23を休日としているのはOutlookです。Gmailはこの日を平日扱いにしています。

 

f:id:gungnir46:20191223132849p:plain


さきほど紹介した2つのウェブページを見てみましょう。holidays-jp.github.ioの方は12/23は平日扱い、calendar-service.netは祝日となっています。そして、Outlookとcarendar-service.netは2020年12月23日も天皇誕生日らしいです。

 

違いが発生した理由その2 即位礼正殿の儀の行われる日

これも皇位継承に伴う例外的な祝日ですね。 

 

Outlook 平日扱い
Gmail 祝日扱い
calendar-service.net 平日扱い
holidays-jp.github.io 祝日扱い

 

違いが発生した理由その3 海の日

そもそも海の日ってなに?っていう話から。

国民の祝日に関する法律祝日法、昭和23年7月20日法律第178号)第2条は、「の恩恵に感謝するとともに、海洋国日本の繁栄を願う」ことを趣旨としている。国土交通省は「世界の国々の中で『海の日』を国民の祝日としている国は唯一日本だけ」[1]としている。

制定当初は7月20日であったが、2003年(平成15年)に改正された祝日法ハッピーマンデー制度により、7月の第3月曜日となった。

(Wikipediaより抜粋)

 

これでいくと2020年の海の日は7月第3月曜日にぶつけて7/20になるはずでした。ところが、2020年は東京オリンピック開催で3連休にすると都内が渋滞するので例外的に7/23に変更することになったらしいです。

2020年令和2年)は東京五輪・パラリンピック特措法により、東京オリンピックの開会式の前日に当たる7月23日(木曜日)に変更される[6]
(Wikipediaより抜粋)

 

一番の問題はこの例外処理に対応しているのはどのカレンダーということです。今回でもOutlookとcalendar-service.netはNG。

Outlook 7月20日
Gmail 7月23日
calendar-service.net 7月20日
holidays-jp.github.io 7月23日

 

2019年の祝日を総チェック

そろそろ分かってきましたが、Outlookとcalendar-serviceはデータソースとしてはダメです。普段は気にも留めていなかった祝日にここまで時間をかけたのは生まれて初めてのことですが、この際なので徹底的に調べてみることにしましょう。

 

  Outlook Gmail calendar-service holidays-jp
04/30 祝日 X O X O
05/01 天皇の即位の日 X O X O
05/02 祝日 X O X O
10/22 即位礼正殿の儀 X O X O
12/23 平日 X O X O

 

 

もはやVBAの技術論ではなく日本国民として知っておくべきチコちゃんネタになってしまいましたが、Outlookの国民祝日カレンダーは使うべきではありません。いますぐ外しましょう。そして、Gmailとholidays-jpに賛辞を贈ることにします。 

 

https://holidays-jp.github.io/api/v1/2020/date.json

出力結果:

{
"2020-01-01": "元日",
"2020-01-13": "成人の日",
"2020-02-11": "建国記念の日",
"2020-02-23": "天皇誕生日",
"2020-02-24": "天皇誕生日 振替休日",
"2020-03-20": "春分の日",
"2020-04-29": "昭和の日",
"2020-05-03": "憲法記念日",
"2020-05-04": "みどりの日",
"2020-05-05": "こどもの日",
"2020-05-06": "憲法記念日 振替休日",
"2020-07-23": "海の日",
"2020-07-24": "体育の日",
"2020-08-10": "山の日",
"2020-09-21": "敬老の日",
"2020-09-22": "秋分の日",
"2020-11-03": "文化の日",
"2020-11-23": "勤労感謝の日"
}

 

Private Sub TestHarness()

    Debug.Print IsHoliday("2019/5/2")

    UpdateHolidays 2019

End Sub

Public Function IsHoliday(pDate As String) As Boolean

    Dim iDay    As Long

    IsHoliday = True
    iDay = Weekday(pDate)
    If iDay = vbSunday Or iDay = vbSaturday Then Exit Function

    Dim vMatch As Variant
    On Error GoTo Unmatch
    vMatch = Application.WorksheetFunction.Match(_
Clng(CDate(pDate)), wksCalendar.Range("A:A"), 0)
IsHoliday = (vMatch > 0) Exit Function Unmatch: IsHoliday = False End Function Public Sub UpdateHolidays(pYear As Long) Dim sURL As String Dim buff As String Dim oStream As Stream Dim sDate As String Dim xCell As Excel.Range ' ---- Download Holidays sURL = "https://holidays-jp.github.io/api/v1/" & pYear & "/date.json" buff = modUtil.GetHTMLText(sURL) Set oStream = New Stream oStream.Buffer = buff Set xCell = wksCalendar.Range("A1") ' ---- Update calendar worksheet Do Until oStream.EOF buff = oStream.ReadLine If InStr(buff, ":") > 0 Then sDate = parseString(buff, """", """") xCell.Value = "'" & Format(sDate, "YYYY/MM/DD") Set xCell = xCell.Offset(1, 0) End If Loop End Sub Private Function parseString(pBuff As String, pKey1 As String, pKey2 As String) As String Dim iPos1 As Long Dim iPos2 As Long iPos1 = InStr(pBuff, pKey1) iPos2 = InStr(iPos1 + 1, pBuff, pKey2) If iPos1 + iPos2 = 0 Then parseString = "" Else iPos1 = iPos1 + Len(pKey1) parseString = Mid(pBuff, iPos1, iPos2 - iPos1) End If End Function