App engine – 지정한 달의 google calendar event 가져오기

Google calendar API 설명문서에는 event list를 가져오는 방법이 설명과 함께 예제 code가 제시되어 있다.

page_token = None
while True:
  events = service.events().list(calendarId='primary', pageToken=page_token).execute()
  for event in events['items']:
    print event['summary']
  page_token = events.get('nextPageToken')
  if not page_token:
    break

이 코드를 수행하면 달력이 가지고 있는 모든 event들을 가져오는데, 이것을 조금 수정해서 지정된 달에 해당하는 event 목록을 가져오는 것으로 변경해 보자.

timeMax / timeMin

가져올 event의 범위는 lower bound를 나타내는 timeMin과 upper bound를 나타내는 timeMax값으로 제한할 수 있는데 이때 사용되는 날짜의 형식은 RFC3339을 따라야 한다.

calendar.monthrange() 

이 method는 년도와 월을 받아서 시작일의 주와 마지막 날짜를 tuple로 반환해 준다. 다음과 같이 특정 달의 마지막 날짜를 구할 수 있다.

>>> import calendar
>>> calendar.monthrange(2002, 06)
(5, 30)
>>> calendar.monthrange(2002, 06)[1]
30

Python에서 RFC3339 표시하기

이 형식은 간단히 말해 날짜를 나타내는 YYYY-MM-DD 형식과 시간을 나타내는 hh:mm:ss가 대문자 ‘T’로 연결되며 그 뒤에는 UTC로 부터의 time zone offset값이 붙는다.

2002-06-30T20:00:00+09:00

datetime.isoformat()을 이용하면 비슷한 형식을 얻을 수는 있으나, 뒤에 붙는 time zone offset이 표시되지 않는다.

>>> datetime(2002, 06, 30, 20, 0, 0).isoformat()
'2002-06-30T20:00:00'

Python에서 time zone을 나타내는 tzinfo class의 설명문에 따르면 tzinfo는 abstract class여서 직접 사용할 수 없고, 이를 상속받는 class를 구현해서 사용해야 한다. 다음은 이 문서에 있는 예제를 조금 수정하여 time zone offset 값을 반환하는 tzinfo의 concrete class를 구현한 것이다.

from datetime import tzinfo, timedelta, datetime

ZERO = timedelta(0)
HOUR = timedelta(hours=1)

class TimeZoneOffset(tzinfo):
    def __init__(self, hours, name):
        self.__offset = timedelta(hours = hours)
        self.__name = name

    def utcoffset(self, dt):
        return self.__offset

    def tzname(self, dt):
        return self.__name

    def dst(self, dt):
        return ZERO

Code

TimeZoneOffset class를 선언한 후에는 datetime()을 호출할때 tzinfo에 TimeZoneOffset의 instance를 넘겨서 time zone offset 정보를 포함하는 RFC3339 날짜 형식을 얻을 수 있다.

# What's the last date of the Jun, 2002?
lastDate = calendar.monthrange(2002, 6)[1]

# Lower bound RFC3339 format '2002-06-01T00:00:00+09:00'
lbound = datetime(2002, 6, 1, tzinfo=TimeZoneOffset(9, "KST")).isoformat()

# Lower bound RFC3339 format '2002-06-01T23:59:59+09:00'
ubound = datetime(2002, 6, lastDate, 23, 59, 59, tzinfo=TimeZoneOffset(9, "KST")).isoformat()

page_token = None
while True:
  # Fetch events within given lower/upper bounds.
  events = service.events().list(calendarId='primary', minTime=lbound, maxTime=ubound, pageToken=page_token).execute()
  for event in events['items']:
    print event['summary']
  page_token = events.get('nextPageToken')
  if not page_token:
    break