月末、月初 (dateutil)

bonlifeです。 id:ymorimoto:20080303:p1 を見て思ったこと。dateutil使えば良いのにっ!(dateutilの中では同じようなことをやってたりするわけですが。)
dateutilモジュールについて「はくじゃめも : dateutil - Pythonで日付の計算」が参考になります。
今月の月初、月末は以下のように取得できます。

In [1]: import datetime

In [2]: from dateutil import relativedelta

In [3]: today = datetime.date.today()

In [4]: today
Out[4]: datetime.date(2008, 3, 13)

In [5]: today + relativedelta.relativedelta(day=1)
Out[5]: datetime.date(2008, 3, 1)

In [6]: today + relativedelta.relativedelta(day=1,months=1,days=-1)
Out[6]: datetime.date(2008, 3, 31)

で、つい先日気付いたのですが、月末は以下のようにしても取得できます!

In [7]: today + relativedelta.relativedelta(day=99)
Out[7]: datetime.date(2008, 3, 31)

これが便利かどうかは別にして、どうやってるの、これ!ってことでdateutilモジュール内の relativedelta.py を少し読んでみました。で、以下の関数に出会う。(...部分は省略です。)

class relativedelta:
    ...
    def __init__(self, dt1=None, dt2=None,
                 years=0, months=0, days=0, leapdays=0, weeks=0,
                 hours=0, minutes=0, seconds=0, microseconds=0,
                 year=None, month=None, day=None, weekday=None,
                 yearday=None, nlyearday=None,
                 hour=None, minute=None, second=None, microsecond=None):
        if dt1 and dt2:
            ...
        else:
            ...
            self.day = day
            ...
    def __radd__(self, other):
        ...
        day = min(calendar.monthrange(year, month)[1],
                  self.day or other.day)
        ...

relativedeltaは2つの生成方法があって、というあたりは省略。__init__() 内の条件分岐で else になって、 self.day には day が入る、と。で、__radd__() の部分!ここで、calendarモジュールの登場ですよ。calendarモジュールの monthrange() に年と月を渡すと、初日の曜日(の数字)と日数のタプルを返してくれます。日数イコール月末日。

In [8]: import calendar

In [9]: calendar.monthrange(2008,2)
Out[9]: (4, 29)

In [10]: calendar.monthrange(2008,3)
Out[10]: (5, 31)

こんな感じ。今年の2月は29日までありましたね、そういや。っと話が逸れました。relativedeltaの self.day には引数で day=n って指定した n が入るわけです。この n と calendar.monthrange(year, month)[1] で取得した月末日の小さい方が day になります。つまり、day=n の n を月末日より大きい値にした場合、最終的に day は月末日になるというマジック!ちゃんと調べてないですけど、翌月1日から1日引くよりも若干速いんじゃないかしら。
何が言いたいのかって言うと、dateutil便利!もっと標準ライブラリ(calendarとか)をちゃんとマスターしたい!ってことです。