月末、月初 (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とか)をちゃんとマスターしたい!ってことです。