【google calendar】Googleさんから日本の祝日取得、管理【叩いてみた】

どうもお久しぶりです

一人暮らしを始めたtenIOIOtenです

タンスも、本棚も古くなっていて、引っ越しに合わせて捨ててしまったので毎日ダンボールを漁る生活をしています

日曜に収納系が届くのですが、週末が2重意味で恋しいですね

さて今回は日本の祝日の取得からキャッシュまでやっていきたいと思います

祝日の取得

まず祝日の取得にはどんな方法があるのか色々調べてみました

結果として祝日の取得にはざっくり以下の3つの方法があることがわかりました

  1. サードパーティapiを使う

  2. マスタを作る

  3. 計算する

それぞれ以下のようなpro conがあります

pro con
サードパーティapiを使う 新しい祝日ができた時などの対応がいらない 仕様が変わるかもしれない。メンテナンスされなくなるかもしれない
マスタを作る 意図しない仕様変更がない、整合性のチェックが容易 定期的に更新をしなくてはならない
計算する 他の2つに比べてメンテナンスコストが低い 祝日の仕様が難しいので、実装コストが高く、いざ仕様変更があった場合は修正がかなり厳しい

今回はメンテの頻度、実装コストの面から、gcpgoogle calendar apiを使うことにしました

Google Calendar API

Google CalendarAPIです

これを使えば、自分の予定一覧をとってこれたり、作成・削除もできます

またcalendarIdを指定してあげれば、権限を与えられてる他人の予定の取得も可能です

課金体系はちょっとよくわかりませんでした

1,000,000 req/dayが割り当てられてるので、それを超えるようなら課金が発生するのかも知れません

手順

Google APIを利用するときは以下の手順を行う必要があります

  1. 有効化
  2. 認証
  3. APIの利用

有効化と認証の方法は探せばgoogleさんが説明してくれるので載せません

認証については少し話をします

認証

gcpapiを使用するには認証を通さなければいけません

認証方法には以下の3つがあります

  • サービスアカウントによる認証

  • ユーザーアカウントによる認証

  • APIキーによる認証

参考

googleさんはサービスアカウントを推しているので、今回はサービスアカウントで実装します

取得、キャッシュ

こんな感じにしました、djangoを使うことを想定してます

from datetime import datetime, timedelta

from django.core.cache import cache
from pytz import timezone

SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
SERVICE_ACCOUNT_FILE = 'hogehoge'

YEAR_RANGE = 1
CALENDAR_ID = 'ja.japanese#holiday@group.v.calendar.google.com'

GOOGLE_DATE_FORMAT = '%Y-%m-%dT%H:%M:%S%z'


class Holiday:

    def get_holidays(self, start_year, end_year=None):
        """
        :param int start_year
        :param int end_year
        :rtype: list(ScheduleJSON)
        """
        holidays = []
        current_year = datetime.now().year

        end_year = end_year or start_year
        for year in range(start_year, end_year + 1):
            if current_year - YEAR_RANGE > year or current_year + YEAR_RANGE < year:
                continue
            tmp_holidays = self.__get_holidays_from_cache(str(year)) or self.__get_holidays_from_google(year)
            holidays.extend(tmp_holidays)

        return holidays

    def __get_holidays_from_cache(self, year):
        """
        :param int year
        :rtype: list(ScheduleJSON)
        """
        json_list = cache.get(str(year))
        return json_list

    def __get_holidays_from_google(self, year):
        """
        :param int year
        :rtype: list(ScheduleJSON)
        """
        obj = self.__calendar_api.events().list(calendarId=CALENDAR_ID, **self.__year_to_range(year)).execute()
        json_list =obj['items']

        cache.set(str(year), json_list)

        return json_list

    def __year_to_range(self, year):
        """
        :param int year: convert to range.
        :rtype: {'timeMin': string, 'timeMax': string}: dict for calendar api
        """
        delta = timedelta(seconds=1)
        timeMin = datetime(year=year, month=1, day=1)
        timeMax = datetime(year=year + 1, month=1, day=1) - delta
        return {'timeMin': timeMin.strftime(GOOGLE_DATE_FORMAT), 'timeMax': timeMax.strftime(GOOGLE_DATE_FORMAT)}

Google Calendarは祝日を現在から前後一年しか持ってません(現在が2019年なら2018/1~2020/12までみたいに)

なので「年単位でfetchする」、「年単位でキャッシュする」などをして調整してます

終わりに

こんな感じで、実装できました

祝日はいつ変わるかわからないので、googleに頼ってしまうのが楽でいいと思います