日本のカレンダーにおける国民の祝日(休日)を、dateオブジェクトのリストで求めるAppleScriptです。
2020〜2021年のイレギュラーな休日変更に対応してみました。
# 何か不具合や不足分があったらおしらせください
# 某・呪われた手帳メーカー系の仕事には不十分かもしれないので、不幸にもその案件に関わった方はメーカー支給の祝祭日データを利用してください
こうした高度なカレンダー計算AppleScriptは、「Newt On Project」の直系の遺産です。自然言語で曜日や日数の指定を行う上で、休日の計算は欠かせないものでした。
休日に関しては政府がCSVファイルで配布しているものもありますが、来年や再来年、5年後のカレンダーを扱いたいといった場合に、自前で計算できないと話になりません。そうした処理に備える意味でも、自前で計算できる「意義」はあります。
AppleScript名:国民の祝日を求める v7.scpt |
use AppleScript use scripting additions use framework "Foundation" (* 本バージョンのテーマ: *) set aList to retHolidayRec(2020) of me–> {{date "2020年1月1日 水曜日 0:00:00", "元旦"}, {date "2020年1月13日 月曜日 0:00:00", "成人の日"}, {date "2020年2月11日 火曜日 0:00:00", "建国記念日"}, {date "2020年2月23日 日曜日 0:00:00", "天皇誕生日"}, {date "2020年2月24日 月曜日 0:00:00", "振替休日"}, {date "2020年3月20日 金曜日 0:00:00", "春分の日"}, {date "2020年5月3日 日曜日 0:00:00", "憲法記念日"}, {date "2020年5月4日 月曜日 0:00:00", "みどりの日"}, {date "2020年5月5日 火曜日 0:00:00", "こどもの日"}, {date "2020年5月6日 水曜日 0:00:00", "振替休日"}, {date "2020年7月22日 水曜日 0:00:00", "海の日"}, {date "2020年7月24日 金曜日 0:00:00", "スポーツの日"}, {date "2020年8月10日 月曜日 0:00:00", "山の日"}, {date "2020年9月21日 月曜日 0:00:00", "敬老の日"}, {date "2020年9月22日 火曜日 0:00:00", "秋分の日"}, {date "2020年11月3日 火曜日 0:00:00", "文化の日"}, {date "2020年11月23日 月曜日 0:00:00", "勤労感謝の日"}} –国民の祝日を求める(属性つき) on retHolidayRec(aYear as integer) –固定の祝日 if aYear < 2020 then set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"4/29", "昭和の日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}} else if aYear = 2020 then set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}} else if aYear = 2021 then set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}} else –2022年から set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}} end if –天皇誕生日の計算 set curEmpBD to getCurEmpBitrthday(aYear) of me if curEmpBD is not equal to false then set the end of holidayList to {curEmpBD, "天皇誕生日"} end if set the end of holidayList to {get_specifiedDay(aYear, 1, 2, 2), "成人の日"} –成人の日–1月の第2月曜日 –令和2年(2020年)及び3年(2021年)における「国民の祝日」の移動について if aYear = 2020 then set the end of holidayList to {"7/ 22", "海の日"} else if aYear = 2021 then set the end of holidayList to {"7/ 23", "海の日"} else set the end of holidayList to {get_specifiedDay(aYear, 7, 2, 3), "海の日"} –海の日–7月の第3月曜日 end if set the end of holidayList to {get_specifiedDay(aYear, 9, 2, 3), "敬老の日"} –敬老の日–9月の第3月曜日 –令和2年(2020年)及び3年(2021年)における「国民の祝日」の移動について if aYear = 2020 then set the end of holidayList to {"7/24", "スポーツの日"} else if aYear = 2021 then set the end of holidayList to {"7/23", "スポーツの日"} else if (aYear < 2000) then set the end of holidayList to {"10/10", "体育の日"} –体育の日–10月10日 else if (aYear < 2020) then set the end of holidayList to {get_specifiedDay(aYear, 10, 2, 2), "体育の日"} –体育の日–10月の第2月曜日 else set the end of holidayList to {get_specifiedDay(aYear, 10, 2, 2), "スポーツの日"} –スポーツの日–10月の第2月曜日 end if –令和2年(2020年)及び3年(2021年)における「国民の祝日」の移動について if aYear = 2020 then set the end of holidayList to {"8/10", "山の日"} else if aYear = 2021 then set the end of holidayList to {"8/8", "山の日"} else if aYear ≥ 2016 then set the end of holidayList to {"8/11", "山の日"} –山の日– 8月11日 end if set the end of holidayList to {"3/" & get_ShunbunNoHi(aYear), "春分の日"} –春分の日 set the end of holidayList to {"9/" & get_ShuubunNoHi(aYear), "秋分の日"} –秋分の日 set holiDate to {} repeat with i in holidayList set holiD to date (aYear & "/" & (item 1 of i) as text) set holiNum to weekday of holiD as number –元日以外を対象とする(元旦に振替休日なし)–> いや、ある(汗) –if ((item 1 of i) as text) is not "1/1" then –振替休日付加処理 if holiNum = 1 then –祝祭日が日曜日だったら –日付を動かすのではなく、振替休日を追加する set holiD_furikae to holiD + (1 * days) set the end of holiDate to {holiD_furikae, "振替休日"} end if –end if set the end of holiDate to {holiD, item 2 of i} end repeat –重複した休日が発生した場合の再振替処理 –基本ルール: 振替休日を後に送る – 「振替休日」が重複リストに入っていないかどうかをチェックし、振替休日の再配置を行う set itemNum to 1 set holiDateDup to detectDuplicatesFromNestedList(holiDate, itemNum) of me set huriList to {} repeat with i in holiDateDup set iCount to length of i repeat with ii in i set {aDate, aDateName} to ii if aDateName = "振替休日" then set the end of huriList to contents of ii end if end repeat end repeat set holiDate to shellSortListAscending(holiDate, 1) of me set holiDateList to spritOrderedItemFromNestedList(holiDate, 1) of me repeat with i in huriList set {aDate, aName} to i set j to contents of i set offsetDate to 1 repeat set bDate to aDate + (offsetDate * days) if bDate is not in holiDateList then exit repeat end if set offsetDate to offsetDate + 1 end repeat set iCount to 1 repeat with ii in holiDate set jj to contents of ii if jj = j then –「複数要素一括削除サブルーチン」などという高機能すぎるサブルーチンを使用。ちょっともったいない set holiDate to itemsDelete(holiDate, {iCount}) of me end if set iCount to iCount + 1 end repeat set the end of holiDate to {bDate, "振替休日"} end repeat –秋分の日と敬老の日の「間の日」の休日判定処理 –参考文献: –http://ja.wikipedia.org/wiki/秋分の日 –国民の祝日に関する法律第3条第3項に規定する休日(例) set septDL to {} set the end of septDL to "9/" & get_ShuubunNoHi(aYear) of me –秋分の日 set the end of septDL to get_specifiedDay(aYear, 9, 2, 3) –敬老の日 –9月の第3月曜日 set septDL to shellSort(septDL) of me if septDL = {"9/21", "9/23"} then set kokuminShukujitu to (aYear as string) & "/9/22" set kokuminShukujitu to date kokuminShukujitu set the end of holiDate to {kokuminShukujitu, "国民の祝日"} end if –重複を解消 set holiDate to removeDuplicates(holiDate) of me –最後に、並べ替えを行って仕上げ set holiDate to shellSortListAscending(holiDate, 1) of me return holiDate end retHolidayRec –春分の日を求める –2000年から2099年の間まで計算可能 on get_ShunbunNoHi(aYear) set a to 20.69115 set b to (aYear – 2000) * 0.2421904 set c to round ((aYear – 2000) / 4) rounding toward zero set d to round (a + b – c) rounding toward zero return d end get_ShunbunNoHi –秋分の日を求める –2000年から2099年の間まで計算可能 on get_ShuubunNoHi(aYear) set a to 23.09 set b to (aYear – 2000) * 0.2421904 set c to round ((aYear – 2000) / 4) rounding toward zero set d to round (a + b – c) rounding toward zero return d end get_ShuubunNoHi –指定月の第x指定曜日に該当する日付を求める(mm/dd形式) – 曜日の指定を数値(weekday of (current date) as number)で行えるようにした。 – 曜日を「日曜日」などの日本語ローカライズド文字列で指定するのをやめた –パラメータ: 年, 月, 曜日番号, 順番 on get_specifiedDay(aYear as integer, aMonth as integer, Youbi as integer, orderNum as integer) set sDat to date ((aYear & "/" & aMonth & "/1") as text) set eDat to getMlenInternational(aYear, aMonth) of me set countNum to 0 repeat with i from 1 to eDat set aCal to date ((aYear & "/" & aMonth & "/" & (i as text)) as text) set aWeekDayNum to weekday of aCal as integer if Youbi = aWeekDayNum then set countNum to countNum + 1 if countNum is orderNum then set aCalText to (aMonth & "/" & i as text) return aCalText end if end if end repeat end get_specifiedDay –指定日の月のみ返す on getMonth(aDat as date) set bDate to month of aDat return bDate as integer end getMonth –指定日の日付のみ返す on getDate(aDat as date) set bDate to day of aDat return bDate as integer end getDate –指定日の年のみ返す on getYear(aDat as date) set bDate to year of aDat return bDate as integer end getYear –現在のカレンダーで指定年月の日数を返す(getMlenから置き換えた) on getMlenInternational(aYear, aMonth) set theNSCalendar to current application’s NSCalendar’s currentCalendar() — do *not* use initWithCalendarIdentifier: set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:1 hour:0 minute:0 |second|:0 nanosecond:0 set theResult to theNSCalendar’s rangeOfUnit:(current application’s NSDayCalendarUnit) inUnit:(current application’s NSMonthCalendarUnit) forDate:theDate return |length| of theResult end getMlenInternational –リスト中から重複項目をリストアップする on detectDuplicates(aList) set aCount to length of aList set duplicationList to {} repeat aCount times set anItem to contents of (first item of aList) set aList to rest of aList if anItem is in aList then set the end of duplicationList to anItem end if end repeat return duplicationList end detectDuplicates –リストから重複部分を除外 on removeDuplicates(aList) set newList to {} repeat with i from 1 to (length of aList) set anItem to item 1 of aList set aList to rest of aList if {anItem} is not in aList then set end of newList to anItem end repeat return newList end removeDuplicates –シェルソート on shellSort(aSortList) script oBj property list : aSortList end script set len to count oBj’s list’s items set gap to 1 repeat while (gap ≤ len) set gap to ((gap * 3) + 1) end repeat repeat while (gap > 0) set gap to (gap div 3) if (gap < len) then repeat with i from gap to (len – 1) set temp to oBj’s list’s item (i + 1) set j to i repeat while ((j ≥ gap) and (oBj’s list’s item (j – gap + 1) > temp)) set oBj’s list’s item (j + 1) to oBj’s list’s item (j – gap + 1) set j to j – gap end repeat set oBj’s list’s item (j + 1) to temp end repeat end if end repeat return oBj’s list end shellSort –シェルソートで入れ子のリストを昇順ソート on shellSortListAscending(a, keyItem) set n to length of a set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1} repeat with h in cols if (h ≤ (n – 1)) then repeat with i from h to (n – 1) set v to item (i + 1) of a set j to i repeat while (j ≥ h) and ((contents of item keyItem of item (j – h + 1) of a) > (item keyItem of v)) set (item (j + 1) of a) to (item (j – h + 1) of a) set j to j – h end repeat set item (j + 1) of a to v end repeat end if end repeat return a end shellSortListAscending –シェルソートで入れ子のリストを降順ソート on shellSortListDecending(a, keyItem) set n to length of a set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1} repeat with h in cols if (h ≤ (n – 1)) then repeat with i from h to (n – 1) set v to item (i + 1) of a set j to i repeat while (j ≥ h) and ((contents of item keyItem of item (j – h + 1) of a) < (item keyItem of v)) set (item (j + 1) of a) to (item (j – h + 1) of a) set j to j – h end repeat set item (j + 1) of a to v end repeat end if end repeat return a end shellSortListDecending –入れ子のリスト中から重複項目をアイテム番号つきでリストアップする on detectDuplicatesFromNestedList(aList, itemNum) set aCount to length of aList copy aList to orig_aList set duplicationList to {} repeat aCount times set anItem to contents of (first item of aList) set aList to rest of aList –指定アイテムだけのリストを毎回再生成して存在確認を行う set aaList to spritOrderedItemFromNestedList(aList, itemNum) of me if (contents of (item itemNum of anItem)) is in aaList then set the end of duplicationList to anItem end if end repeat –検出した重複データを元に、該当するデータをリストアップ set detectList to {} repeat with i in duplicationList set j to contents of (item itemNum of i) set detectItem to {} repeat with ii in orig_aList set jj to contents of (item itemNum of ii) if jj = j then set the end of detectItem to (contents of ii) end if end repeat set the end of detectList to detectItem end repeat return detectList end detectDuplicatesFromNestedList –入れ子のリストの全要素から指定アイテム目の要素だけを取り出してリストで返す on spritOrderedItemFromNestedList(aList, itemNum) set aaList to {} repeat with i in aList set the end of aaList to contents of (item itemNum of i) end repeat return aaList end spritOrderedItemFromNestedList –リスト中の指定要素を削除して返す on itemsDelete(aList, delNumList) set delLen to length of delNumList repeat with i from 1 to delLen set newList to {} set aLen to length of aList set ii to item i of delNumList if ii = 1 then set maeList to items 2 thru aLen of aList set newList to maeList else if ii = aLen then set maeList to items 1 thru (aLen – 1) of aList set newList to maeList else set maeList to items 1 thru (ii – 1) of aList set atoList to items (ii + 1) thru -1 of aList set newList to maeList & atoList end if –アイテム指定の補正 set delNumList to adjustItemNo(ii, delNumList) of me set aList to newList end repeat return newList end itemsDelete –itemsDeleteのサブルーチン –リストに対して複数アイテムの削除を行う場合に、1つ削除した後にはアイテム指定が –狂ってしまうため、毎回削除するたびにアイテム指定の補正を行う –paramNumとelemListの間でのパラメータの衝突は関知しない –項目要素補正をリストに対して行う on adjustItemNo(paramNum, elemList) –項目ゼロを指定してきた場合には、そのままelemListを戻す if paramNum = 0 then return elemList –プラス方向のレンジ外判定は行っていない。elemListのlengthよりも大きな値は関知しない set retList to {} repeat with i in elemList set j to contents of i if j > paramNum then set ansNum to j – 1 else set ansNum to j end if set the end of retList to ansNum end repeat return retList end adjustItemNo –現在のカレンダーで指定年月のdate objectを返す(年、月、日、時、分、秒) on getDateInternationalYMDhms(aYear, aMonth, aDay, anHour, aMinute, aSecond) set theNSCalendar to current application’s NSCalendar’s currentCalendar() set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:anHour minute:aMinute |second|:aSecond nanosecond:0 return theDate as date end getDateInternationalYMDhms –現在のカレンダーで指定年月のdate objectを返す(年、月、日) on getDateInternational(aYear, aMonth, aDay) set theNSCalendar to current application’s NSCalendar’s currentCalendar() set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:0 minute:0 |second|:0 nanosecond:0 return theDate as date end getDateInternational –天皇誕生日の計算 on getCurEmpBitrthday(targYear) –昭和、平成、令和 の誕生日定義 set curEmperrorsBirthday to {{"4/29", {1926, 1988}}, {"12/23", {1989, 2018}}, {"2/23", {2020, 9999}}} –浩宮氏が崩御の際には、崩御年を記入 set hitF to false repeat with i in curEmperrorsBirthday copy i to {targDate, {beginYear, endYear}} if targYear ≥ beginYear and targYear ≤ endYear then set hitF to true exit repeat end if end repeat if hitF = false then return false end if return targDate end getCurEmpBitrthday |