8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

zshで祝日取得

Last updated at Posted at 2014-04-04

zshで祝日計算

20140405追記

  • 結局、Appleのicsを採用した
  • GoogleCalendarだと銀行休業日なんてものがあっていいかなと思った
  • クリスマスまで祝日になってて、日本では休日じゃないですしおすしってなった

概要

  • Perl で何回か書いたことあったけど、シェルスクリプトで年末くらいにトライした
  • きっかけは第N曜日って cal コマンドから簡単にとれるんじゃないか?
  • よし!作れる(確信)

思い出す

  • パースする条件ってなんだっけ?ということで
$ curl -s 'http://www.mozilla.org/projects/calendar/caldata/JapanHolidays.ics' | cut -d: -f1 | sort | uniq
 Day
BEGIN
CATEGORIES
CREATED
DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Asia/Tokyo
DTSTAMP
DTSTART
DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Asia/Tokyo
END
LAST-MODIFIED
PRODID
RRULE
SUMMARY
TZID
TZNAME
TZOFFSETFROM
TZOFFSETTO
UID
VERSION
X-LIC-LOCATION

おkおk、とりあえず、祝日を計算するだけなら次の2つをだけみればいい

  • DTSTART
  • 祝日、もしくは繰り返し条件がある場合の最初の時間
  • RULE
  • 繰り返し条件、後述

RRULE をパースするのでパターン分け

  • おもむろにRRULEだけ抽出
 curl -s 'http://www.mozilla.org/projects/calendar/caldata/JapanHolidays.ics' | grep RRULE
$ curl -s 'http://www.mozilla.org/projects/calendar/caldata/JapanHolidays.ics' | grep RRULE | sort | uniq
RRULE:FREQ=MONTHLY;INTERVAL=12;BYDAY=2MO
RRULE:FREQ=MONTHLY;INTERVAL=12;BYDAY=3MO
RRULE:FREQ=YEARLY;COUNT=3;INTERVAL=4
RRULE:FREQ=YEARLY;COUNT=4;INTERVAL=4
RRULE:FREQ=YEARLY;INTERVAL=1

なし

  • これは楽、無条件で DTSTART が実行日(大体、振替休日)

RRULE:FREQ=MONTHLY;INTERVAL=12;BYDAY=2MO

RRULE:FREQ=MONTHLY;INTERVAL=12;BYDAY=3MO

  • DTSTARTの月の第N曜日が祝日

RRULE:FREQ=YEARLY;COUNT=4;INTERVAL=4

RRULE:FREQ=YEARLY;COUNT=3;INTERVAL=4

  • 4年間隔でCOUNT回繰り返す祝日

RRULE:FREQ=YEARLY;INTERVAL=1

  • 毎年、同じ月日が祝日

抽出条件を整理

RRULEがないケース

  • 年が一致していればDTSTARTの月日を返す

: 以下を ; で切った結果、要素が2つ

  • DTSTARTの月日を返す

: 以下を ; で切った結果、要素が3つ

  • YEARLY のケース
  • COUNTINTERBAL を加算し続けて年が一致すれば
    DTSTART の月日を返す
  • MONTHLY のケース
  • DTSTARTの月の第N曜日を返す

引数

  • 年は必要
  • 月もあってもいいかもくらい(grepすれば対象月だけとれるし)

実装

#!/usr/bin/zsh

readonly YEAR=$1
typeset -A DoW
DoW=('SU' 1 'MO' 2 'TU' 3 'WE' 4 'TH' 5 'FR' 6 'SA' 7)

set -ue

function get_Nth_wday () {
    integer Nth="$1"
    local dow=${DoW[$2]}
    integer mm="$3"
    echo "$(printf '%02d' $(ncal "${mm}" "${YEAR}" | tail -n+2 | tail -n+"$dow" | head -n1 | sed 's/  */ /g' | cut -d' ' -f$(expr 1 + ${Nth})))"
}

function parse_ics () {
    integer dtstart=$1
    typeset -a rrule
    rrule=($(echo "$2" | sed 's/;/ /g'))

    if [ "${#rrule}" -eq 2 ]; then
        echo "${dtstart}" | sed 's/^[0-9]\{4\}//'
    elif [[ "${rrule}" =~ 'YEARLY' ]] ; then
        [[ "${rrule}" =~ 'COUNT=([0-9]*)' ]]
        integer count=${match[1]}

        [[ "${rrule}" =~ 'INTERVAL=([0-9]*)' ]]
        integer interval=${match[1]}

        [[ "${dtstart}" =~ '(....)(....)' ]]
        integer yyyy=${match[1]}
        local mmdd="${match[2]}"

        for ((i=0; i <= $count; i += 1))
        do
            yyyy=$(expr "${yyyy}" + "$interval")
            if [ $yyyy -eq $YEAR ] ; then
                echo "${dtstart}" | sed 's/^....//'
            fi
        done
    else
        [[ "${rrule}" =~ 'BYDAY=(.)(..)' ]]
        integer Nth="${match[1]}"
        local dow="${match[2]}"

        [[ "${dtstart}" =~ '....(..)..' ]]
        local mm=$match[1]
        echo "${mm}$(get_Nth_wday ${Nth} ${dow} ${mm})"
    fi
}

() {
    typeset -A holiday
    local holidays

    wget -q http://ical.mac.com/ical/Japanese32Holidays.ics -O - | \
    while read result
    do :
        if [[ "${result}" =~ "^BEGIN:VEVENT" ]] ; then
            :
            holiday=(
                rrule ''
                dtstart ''
                summary ''
            )
        elif [[ "${result}" =~ "^RRULE:(.*)" ]] ; then
            holiday[rrule]="${match[1]}"
        elif [[ "${result}" =~ "^DTSTART;.*:([0-9]*)" ]] ; then
            holiday[dtstart]="${match[1]}"
        elif [[ "${result}" =~ "^SUMMARY" ]] ; then
            holiday[summary]="$(echo ${result} | cut -d: -f2 | cut -d' ' -f1)"
        elif [[ "${result}" =~ "^END:VEVENT" ]] ; then
            if [[ "${holiday[dtstart]}" = '' ]] ; then
                continue
            elif [[ "${holiday[rrule]}" = '' ]] ; then
                if [[ "${holiday[dtstart]}" =~ "^${YEAR}" ]] ; then
                    [[ "${holiday[dtstart]}" =~ '....(....)' ]]
                    holidays="$holidays\n${YEAR}${match[1]}: ${holiday[summary]}"
                fi
            else
                local mmdd="$(parse_ics "${holiday[dtstart]}" "${holiday[rrule]}")"

                if [[ "${mmdd}" != '' ]] ; then
                    holidays="$holidays\n${YEAR}${mmdd}: ${holiday[summary]}"
                fi
            fi
        fi
    done

    echo "${holidays}" | sort
}

実行結果(Apple)

  • 銀行休業日とりたかったなー
$ zsh holidays.sh 2015

20150101: 元日
20150112: 成人の日
20150211: 建国記念の日
20150321: 春分の日
20150429: 昭和の日
20150503: 憲法記念日
20150504: みどりの日
20150505: こどもの日
20150506: 振替休日
20150720: 海の日
20150921: 敬老の日
20150922: 国民の休日
20150923: 秋分の日
20151012: 体育の日
20151103: 文化の日
20151123: 勤労感謝の日
20151223: 天皇誕生日

$ zsh holidays.sh 2014

20140101: 元日
20140113: 成人の日
20140211: 建国記念の日
20140321: 春分の日
20140429: 昭和の日
20140503: 憲法記念日
20140504: みどりの日
20140505: こどもの日
20140506: 振替休日
20140721: 海の日
20140915: 敬老の日
20140923: 秋分の日
20141013: 体育の日
20141103: 文化の日
20141123: 勤労感謝の日
20141124: 振替休日
20141223: 天皇誕生日

実行結果(GoogleCalendar)

  • 年を食わせてあげる
$ zsh holidays.sh 2014

20140101: 元日
20140102: 銀行休業日
20140103: 銀行休業日
20140113: 成人の日
20140211: 建国記念の日
20140321: 春分の日
20140429: 昭和の日
20140503: 憲法記念日
20140504: みどりの日
20140505: こどもの日
20140506: みどりの日
20140721: 海の日
20140915: 敬老の日
20140923: 秋分の日
20141013: 体育の日
20141103: 文化の日
20141123: 勤労感謝の日
20141124: 勤労感謝の日
20141223: 天皇誕生日
20141225: クリスマス
20141231: 大晦日
$ zsh holidays.sh 2015

20150101: 元日
20150102: 銀行休業日
20150103: 銀行休業日
20150112: 成人の日
20150211: 建国記念の日
20150321: 春分の日
20150429: 昭和の日
20150503: 憲法記念日
20150504: みどりの日
20150505: こどもの日
20150506: 憲法記念日
20150720: 海の日
20150921: 敬老の日
20150922: 国民の休日
20150923: 秋分の日
20151012: 体育の日
20151103: 文化の日
20151123: 勤労感謝の日
20151223: 天皇誕生日
20151225: クリスマス

実行環境

$ cat /etc/debian_version
7.1
8
8
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?