Python で前月同日・前月末日を求める


業務系のシステム作ると必ず出てくる「前月同日」「前月末日」。
ちょくちょく使いそうなのでメモ。



前月同日

先日悩んでいた所、素敵な助言をいただきました。



というわけで、 dateutil を使用。
このライブラリ、日付計算以外にもタイムゾーンの指定も楽だったりして、かなりの神ライブラリです。

>>> import datetime
>>> from dateutil import relativedelta

>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2011, 8, 14, 15, 2, 48, 6994)

>>> now - relativedelta.relativedelta(months=1)
datetime.datetime(2011, 7, 14, 15, 2, 48, 6994)


ちなみに、3月31日などの前月同日が存在しない場合、

民法 第143条第2項(暦による期間の計算)

週、月又は年の初めから期間を起算しないときは、その期間は、最後の週、月又は年においてその起算日に応当する日の前日に満了する。ただし、月又は年によって期間を定めた場合において、最後の月に応当する日がないときは、その月の末日に満了する。

民法第143条第2項(暦による期間の計算)

との事で、これもまた dateutil は対応出来てます。

>>> import datetime
>>> from dateutil import relativedelta

>>> datetime.datetime(2011, 3, 31) - relativedelta.relativedelta(months=1)
datetime.datetime(2011, 2, 28, 0, 0)


まさに神。


前月末日


前月同日から応用して、前月末日。

>>> import datetime
>>> import calendar
>>> from dateutil import relativedelta

>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2011, 8, 14, 16, 48, 29, 526716)

>>> last = now - relativedelta.relativedelta(months=1)
>>> last
datetime.datetime(2011, 7, 14, 16, 48, 29, 526716)

>>> weekday, last_day = calendar.monthrange(last.year, last.month)
>>> weekday, last_day
(4, 31)

>>> last_date = datetime.datetime(last.year, last.month, last_day)
>>> last_date
datetime.datetime(2011, 7, 31, 0, 0)


これはもうちょっと上手く出来そうかな…?
ちなみに、calendar.monthrange(year, month) は指定した年月の1日の曜日と末日を求めるメソッド。


関数化して、その辺に置いとけば便利に使えそう。

import datetime
import calendar
from dateutil import relativedelta


def last_date(year, month, day):
  now = datetime.datetime(year, month, day)
  return now - relativedelta.relativedelta(months=1)


def last_day(year, month):
  last = last_date(year, month, 1)
  end_day = calendar.monthrange(last.year, last.month)[1]
  return datetime.datetime(last.year, last.month, end_day)
>>> last_date(2011, 8, 15)
datetime.datetime(2011, 7, 15, 0, 0)

>>> last_day(2011, 8)
datetime.datetime(2011, 7, 31, 0, 0)


教えて頂いた Ian さんに多大なる感謝を捧げます!