月の最終日を取得する方法は?


633

Pythonの標準ライブラリを使用して、特定の月の最終日を簡単に決定する(つまり、1回の関数呼び出し)方法はありますか?

標準ライブラリがサポートしていない場合、dateutilパッケージはこれをサポートしていますか?

回答:


1083

モジュールドキュメントをcalendar見ていたときに、これに気づきませんでしたが、呼び出されたメソッドmonthrangeがこの情報を提供します。

monthrange(year、month)
    指定された年と月について、月の最初の曜日と月の日数を返します。

>>> import calendar
>>> calendar.monthrange(2002,1)
(1, 31)
>>> calendar.monthrange(2008,2)
(4, 29)
>>> calendar.monthrange(2100,2)
(0, 28)

そう:

calendar.monthrange(year, month)[1]

最も簡単な方法のようです。

明確にmonthrangeするために、うるう年もサポートしています:

>>> from calendar import monthrange
>>> monthrange(2012, 2)
(2, 29)

私の以前の答えはまだ機能しますが、明らかに次善です。


これは元の質問に対する正しい答えではありません!月の最終日が土日である場合はどうなりますか。
Mahdi 2017

8
@mahdi:正解です。2番目の数字は、「その月のnrの日数」==「最終日」です。これは、どのような日であるかには関係ありません。
RickyA 2017

1800年2月の間違った日を返します。
わかり


monthrange混乱する名前を考えるのは私だけではありません。あなたはそれが戻ってくると思うだろう(first_day, last_day)、ではない(week_day_of_first_day, number_of_days)
Flimm

146

calendarモジュールをインポートしたくない場合は、簡単な2ステップ関数を次のようにすることもできます。

import datetime

def last_day_of_month(any_day):
    next_month = any_day.replace(day=28) + datetime.timedelta(days=4)  # this will never fail
    return next_month - datetime.timedelta(days=next_month.day)

出力:

>>> for month in range(1, 13):
...     print last_day_of_month(datetime.date(2012, month, 1))
...
2012-01-31
2012-02-29
2012-03-31
2012-04-30
2012-05-31
2012-06-30
2012-07-31
2012-08-31
2012-09-30
2012-10-31
2012-11-30
2012-12-31

81

編集:よりきれいな解決策については@ブレアコンラッドの答えを見てください


>>> import datetime
>>> datetime.date(2000, 2, 1) - datetime.timedelta(days=1)
datetime.date(2000, 1, 31)

43
12月に失敗してtoday.month + 1 == 13を取得するという事実を除いて、私は実際にこのクリーナーを呼び出しますValueError
fletom 2014年

4
(today.month % 12) + 1それ12 % 12は0 を使用して解決できます
bluesmoon

月の31日にある奇妙な夏時間スイッチ(今日は存在しないと思いますが、将来は異なる場合があります)は、その月の31を23時間の長さでレンダリングする可能性があるため、1日を減算すると、 30日の23:00:00に終了します。確かにこれは奇抜なケースですが、このアプローチが適切でないことを示しています。
Alfe

50

これは実際にはdateutil.relativedelta(pip用のpython-datetutilパッケージ)でかなり簡単です。day=31常に常に月の最終日が返されます。

例:

from datetime import datetime
from dateutil.relativedelta import relativedelta

date_in_feb = datetime.datetime(2013, 2, 21)
print datetime.datetime(2013, 2, 21) + relativedelta(day=31)  # End-of-month
>>> datetime.datetime(2013, 2, 28, 0, 0)

7
個人的に、relativedelta(months = + 1、seconds = -1)が好きなのは、何が起こっているのかがより明白に思えます
BenH

あなたが間違っている。datetime(2014, 2, 1) + relativedelta(days=31)与えるdatetime(2014, 3, 4, 0, 0)...
エマニュエル

9
day =ではなくdays =を使用した
Vince Spicer 2014

@BenHこれは、最初にmonthsが追加され、次にseconds減算されるという希望に基づいています(指定されているかどうかはわかりません)。それらは、**kwargs私がそれに依存せず、一連の個別の操作を行うように渡されるため、now + relative(months=+1) + relative(day=1) + relative(days=-1)明確です。
-Alfe、

last_day = (<datetime_object> + relativedelta(day=31)).day
user12345

44

編集:私の他の回答を参照しください。この実装よりも優れた実装があります。誰かが「自分の」計算機をどのように「ロール」するかを知りたい場合に備えて、ここに残します。

@John Millikinは良い答えを示しており、翌月の最初の日を計算するという複雑さが加わっています。

以下は特にエレガントではありませんが、特定の日付が存在する月の最終日を把握するには、次のようにしてみてください。

def last_day_of_month(date):
    if date.month == 12:
        return date.replace(day=31)
    return date.replace(month=date.month+1, day=1) - datetime.timedelta(days=1)

>>> last_day_of_month(datetime.date(2002, 1, 17))
datetime.date(2002, 1, 31)
>>> last_day_of_month(datetime.date(2002, 12, 9))
datetime.date(2002, 12, 31)
>>> last_day_of_month(datetime.date(2008, 2, 14))
datetime.date(2008, 2, 29)

ばかげているように聞こえますが、このようにして月の初日を同様に取得するにはどうすればよいですか。
Kishan

10
いつも1じゃないですか?
ブレアコンラッド

ええ、でも私は混乱していました:start_date = date(datetime.now()。year、datetime.now()。month、1)
Kishan

4
ああ。today = datetime.date.today(); start_date = today.replace(day=1)。12月31日の午前0時の直前と、午前0時の直後に呼び出した場合に備えて、datetime.nowを2回呼び出さないようにする必要があります。2016-12-01または2017-01-01の代わりに2016-01-01を取得します。
ブレアコンラッド

これは、日時インスタンスを理解して返すことは非常に簡単で、多くの場合に役立ちます。もう一つの利点は、入力があれば、それが動作することであるdateのインスタンスでdatetime.datedatetime.datetimeまたしてpandas.Timestamp
ギルヘルメサロメ

26

dateutil.relativedeltaあなたを使用すると、このような月の最後の日付が得られます:

from dateutil.relativedelta import relativedelta
last_date_of_month = datetime(mydate.year, mydate.month, 1) + relativedelta(months=1, days=-1)

アイデアは、月の最初の日を取得し、使用relativedeltaして1か月前に1日戻って希望の月の最終日を取得することです。


13

別の解決策は次のようなことをすることです:

from datetime import datetime

def last_day_of_month(year, month):
    """ Work out the last day of the month """
    last_days = [31, 30, 29, 28, 27]
    for i in last_days:
        try:
            end = datetime(year, month, i)
        except ValueError:
            continue
        else:
            return end.date()
    return None

次のような関数を使用します。

>>> 
>>> last_day_of_month(2008, 2)
datetime.date(2008, 2, 29)
>>> last_day_of_month(2009, 2)
datetime.date(2009, 2, 28)
>>> last_day_of_month(2008, 11)
datetime.date(2008, 11, 30)
>>> last_day_of_month(2008, 12)
datetime.date(2008, 12, 31)

5
複雑すぎて、Pythonの禅の3番目のルールに違反します。
markuz、2011

11
from datetime import timedelta
(any_day.replace(day=1) + timedelta(days=32)).replace(day=1) - timedelta(days=1)

これがバグの原因です;)1月31日から試してください
LeartS

@LeartS:それは私のために働きます。しようとするとどうなりますか?
Collin Anderson

1
できます。any_dayは1月31日なので、1日を1に置き換えます。したがって、1月1日は32日を追加し、2月2日を取得し、再びday = 1に置き換えて、2月1日を取得します。1日を減算すると、1月31日が取得されます。表示されません。問題は何ですか。何曜日になりますか?
コリンアンダーソン

9
>>> import datetime
>>> import calendar
>>> date  = datetime.datetime.now()

>>> print date
2015-03-06 01:25:14.939574

>>> print date.replace(day = 1)
2015-03-01 01:25:14.939574

>>> print date.replace(day = calendar.monthrange(date.year, date.month)[1])
2015-03-31 01:25:14.939574

9

外部ライブラリを使用する場合は、http://crsmithdev.com/arrow/をチェックしてください。

Uは、次のコマンドで月の最終日を取得できます。

import arrow
arrow.utcnow().ceil('month').date()

これにより、日付オブジェクトが返され、操作を行うことができます。


9

月の最終日を取得するには、次のようにします。

from datetime import date, timedelta
import calendar
last_day = date.today().replace(day=calendar.monthrange(date.today().year, date.today().month)[1])

ここで何をしているのかを説明するために、2つの部分に分けます。

最初は、ブレアコンラッドが彼のソリューションについてすでに述べたmonthrangeを使用する当月の日数を取得することです。

calendar.monthrange(date.today().year, date.today().month)[1]

2番目は、最後の日付自体を取得することです。たとえば、replaceを使用して行います。

>>> date.today()
datetime.date(2017, 1, 3)
>>> date.today().replace(day=31)
datetime.date(2017, 1, 31)

そして、上で述べたようにそれらを組み合わせると、動的なソリューションが得られます。


このコードは問題の解決に役立つ可能性がありますが、なぜまたはどのように質問に答えるは説明していません。この追加のコンテキストを提供すると、その長期的な教育的価値が大幅に向上します。回答を編集して、適用される制限や前提条件などの説明を追加してください。
Toby Speight 2016

これは最も簡単なようです。もしも2行に分けて書いているのならdate.replace(day=day)、誰もが何が起こっているのかを理解できるように、良いものを得ることができます。
Adam Starrh

9

Python 3.7には、文書化されていないcalendar.monthlen(year, month)関数あります

>>> calendar.monthlen(2002, 1)
31
>>> calendar.monthlen(2008, 2)
29
>>> calendar.monthlen(2100, 2)
28

文書化されたcalendar.monthrange(year, month)[1]呼び出しと同等です。


1
これはPython 3.8.1では機能しないようです:AttributeError:モジュール 'calendar'に属性 'monthlen'がありません
jeppoo1

1
@ jeppoo1:はい、それはbpo-28292でプライベートとしてマークされています-文書化されていない関数に期待されます。
jfs

5
import datetime

now = datetime.datetime.now()
start_month = datetime.datetime(now.year, now.month, 1)
date_on_next_month = start_month + datetime.timedelta(35)
start_next_month = datetime.datetime(date_on_next_month.year, date_on_next_month.month, 1)
last_day_month = start_next_month - datetime.timedelta(1)

5

パンダを使用してください!

def isMonthEnd(date):
    return date + pd.offsets.MonthEnd(0) == date

isMonthEnd(datetime(1999, 12, 31))
True
isMonthEnd(pd.Timestamp('1999-12-31'))
True
isMonthEnd(pd.Timestamp(1965, 1, 10))
False

1
pd.series.dt.is_month_end リンク
Pablo

1
パンダのdatetimeオブジェクトそのための具体的な方法があります:now=datetime.datetime.now(); pd_now = pd.to_datetime(now); print(pd_now.days_in_month)
Roei Bahumi

3

私にとってそれは最も簡単な方法です:

selected_date = date(some_year, some_month, some_day)

if selected_date.month == 12: # December
     last_day_selected_month = date(selected_date.year, selected_date.month, 31)
else:
     last_day_selected_month = date(selected_date.year, selected_date.month + 1, 1) - timedelta(days=1)

3

最も簡単な方法(カレンダーをインポートする必要がない)は、翌月の最初の日を取得し、その日から1日を引くことです。

import datetime as dt
from dateutil.relativedelta import relativedelta

thisDate = dt.datetime(2017, 11, 17)

last_day_of_the_month = dt.datetime(thisDate.year, (thisDate + relativedelta(months=1)).month, 1) - dt.timedelta(days=1)
print last_day_of_the_month

出力:

datetime.datetime(2017, 11, 30, 0, 0)

PS:このコードは、import calendarアプローチに比べて高速に実行されます。下記参照:

import datetime as dt
import calendar
from dateutil.relativedelta import relativedelta

someDates = [dt.datetime.today() - dt.timedelta(days=x) for x in range(0, 10000)]

start1 = dt.datetime.now()
for thisDate in someDates:
    lastDay = dt.datetime(thisDate.year, (thisDate + relativedelta(months=1)).month, 1) - dt.timedelta(days=1)

print ('Time Spent= ', dt.datetime.now() - start1)


start2 = dt.datetime.now()
for thisDate in someDates:
    lastDay = dt.datetime(thisDate.year, 
                          thisDate.month, 
                          calendar.monthrange(thisDate.year, thisDate.month)[1])

print ('Time Spent= ', dt.datetime.now() - start2)

出力:

Time Spent=  0:00:00.097814
Time Spent=  0:00:00.109791

このコードは、月の最終日の日付(つまり、DD部分だけでなく、YYYYMMDDの日付全体)が必要であることを前提としています。


なぜあなたはしたくないのですimport calendarか?
Adam Venturella 2017年

1
それはより速いので。これを含めるために上記の私の回答を変更しました。
Vishal 2017年

@Vishal概念は正しいですが、次の行は正しくありませんでした: `` `dt.datetime(thisDate.year、(thisDate + relativedelta(months = 1))。month、1)-dt.timedelta(days = 1)` ``特に月が年末にある場合。代わりに `` `last_date_of_month = \ first_date_of_month + relativedelta(months = 1)-relativedelta(days = 1)` ``を
試してください

3

ここに別の答えがあります。追加のパッケージは必要ありません。

datetime.date(year + int(month/12), month%12+1, 1)-datetime.timedelta(days=1)

翌月の最初の日を取得し、それから1日を引きます。


2

終了日は自分で計算できます。単純なロジックは、翌月のstart_dateから1日を引くことです。:)

カスタムメソッドを書いて、

import datetime

def end_date_of_a_month(date):


    start_date_of_this_month = date.replace(day=1)

    month = start_date_of_this_month.month
    year = start_date_of_this_month.year
    if month == 12:
        month = 1
        year += 1
    else:
        month += 1
    next_month_start_date = start_date_of_this_month.replace(month=month, year=year)

    this_month_end_date = next_month_start_date - datetime.timedelta(days=1)
    return this_month_end_date

呼び出し、

end_date_of_a_month(datetime.datetime.now().date())

今月の終了日を返します。この関数に任意の日付を渡します。その月の終了日を返します。



1

これは、標準の日時ライブラリだけを使用する私にとって最も簡単なソリューションです。

import datetime

def get_month_end(dt):
    first_of_month = datetime.datetime(dt.year, dt.month, 1)
    next_month_date = first_of_month + datetime.timedelta(days=32)
    new_dt = datetime.datetime(next_month_date.year, next_month_date.month, 1)
    return new_dt - datetime.timedelta(days=1)

0

これは主な質問には対応していませんが、月の最後の平日を取得する1つの優れたトリックは、を使用することcalendar.monthcalendarです。

# Some random date.
some_date = datetime.date(2012, 5, 23)

# Get last weekday
last_weekday = np.asarray(calendar.monthcalendar(some_date.year, some_date.month))[:,0:-2].ravel().max()

print last_weekday
31

すべて[0:-2]は週末の列を削り取り、捨てることです。月の外にある日付は0で示されるため、最大値は事実上それらを無視します。

使用はnumpy.ravel厳密には必要ではないですが、私はに頼る嫌い単なる慣習numpy.ndarray.maxを超える計算に軸線どの言われていない場合は、配列をフラット化します。


0
import calendar
from time import gmtime, strftime
calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]

出力:

31



これにより、現在の月が何であるかの最終日が出力されます。この例では、2016年5月15日でした。したがって、出力は異なる場合がありますが、出力は現在の月と同じ日数になります。毎日のcronジョブを実行して月の最終日を確認する場合に最適です。

そう:

import calendar
from time import gmtime, strftime
lastDay = calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]
today = strftime("%d", gmtime())
lastDay == today

出力:

False

月末日でない限り。


0

私はこの方法を好む

import datetime
import calendar

date=datetime.datetime.now()
month_end_date=datetime.datetime(date.year,date.month,1) + datetime.timedelta(days=calendar.monthrange(date.year,date.month)[1] - 1)

0

独自の小さな関数を作成したい場合、これは良い出発点です:

def eomday(year, month):
    """returns the number of days in a given month"""
    days_per_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    d = days_per_month[month - 1]
    if month == 2 and (year % 4 == 0 and year % 100 != 0 or year % 400 == 0):
        d = 29
    return d

このため、うるう年のルールを知っておく必要があります。

  • 4年ごと
  • 100年ごとを除いて
  • しかし、400年ごとに

1
もちろん、システムライブラリにはすでにこのデータがあり、ライブラリの更新を宣言することによってルールを変更する必要がある場合は、他の誰かの問題です。
Jasen

まあ、可能ですが、可能性は非常に低いですが、たとえそれが-約2000年であなたを噛むだけだったとしても... en.wikipedia.org/wiki/Gregorian_calendar#Accuracy
mathause

これらのルールは、私は彼らが未来に何千年もの間に立つだろうという自信を持っていない過去に500年のための仕事をしません
Jasen

0

日付範囲を渡す場合、これを使用できます。

def last_day_of_month(any_days):
    res = []
    for any_day in any_days:
        nday = any_day.days_in_month -any_day.day
        res.append(any_day + timedelta(days=nday))
    return res

0

以下コードでは、「get_last_day_of_month(dt)」がこれを提供し、日付は「YYYY-MM-DD」のような文字列形式になります。

import datetime

def DateTime( d ):
    return datetime.datetime.strptime( d, '%Y-%m-%d').date()

def RelativeDate( start, num_days ):
    d = DateTime( start )
    return str( d + datetime.timedelta( days = num_days ) )

def get_first_day_of_month( dt ):
    return dt[:-2] + '01'

def get_last_day_of_month( dt ):
    fd = get_first_day_of_month( dt )
    fd_next_month = get_first_day_of_month( RelativeDate( fd, 31 ) )
    return RelativeDate( fd_next_month, -1 )

0

最も簡単な方法は、datetime日付の計算を使用することです。たとえば、翌月の最初の日から1日を減算します。

import datetime

def last_day_of_month(d: datetime.date) -> datetime.date:
    return (
        datetime.date(d.year + d.month//12, d.month % 12 + 1, 1) -
        datetime.timedelta(days=1)
    )

または、を使用calendar.monthrange()して(うるう年を考慮に入れて)月の日数を取得し、それに応じて日付を更新することもできます。

import calendar, datetime

def last_day_of_month(d: datetime.date) -> datetime.date:
    return d.replace(day=calendar.monthrange(d.year, d.month)[1])

簡単なベンチマークは、最初のバージョンが著しく速いことを示しています:

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

In [15]: %timeit last_day_of_month_dt(today)
918 ns ± 3.54 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [16]: %timeit last_day_of_month_calendar(today)
1.4 µs ± 17.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

0

これは長い(理解しやすい)バージョンですが、うるう年を扱います。

乾杯、JK

def last_day_month(year, month):
    leap_year_flag = 0
    end_dates = {
        1: 31,
        2: 28,
        3: 31,
        4: 30,
        5: 31,
        6: 30,
        7: 31,
        8: 31,
        9: 30,
        10: 31,
        11: 30,
        12: 31
    }

    # Checking for regular leap year    
    if year % 4 == 0:
        leap_year_flag = 1
    else:
        leap_year_flag = 0

    # Checking for century leap year    
    if year % 100 == 0:
        if year % 400 == 0:
            leap_year_flag = 1
        else:
            leap_year_flag = 0
    else:
        pass

    # return end date of the year-month
    if leap_year_flag == 1 and month == 2:
        return 29
    elif leap_year_flag == 1 and month != 2:
        return end_dates[month]
    else:
        return end_dates[month]

削除することelse: passif year % 400 == 0: leap_year_flag = 1、マイナーな変更で取り除くこともできます
canbax

-1

以下は、ソリューションベースのpythonラムダです。

next_month = lambda y, m, d: (y, m + 1, 1) if m + 1 < 13 else ( y+1 , 1, 1)
month_end  = lambda dte: date( *next_month( *dte.timetuple()[:3] ) ) - timedelta(days=1)

next_monthラムダは、タプルの翌月の初日の表現、そして来年にロールオーバーを検出します。month_endラムダは、日付(変換しdte、タプルへ)を適用next_monthして、新しい日付を作成します。その場合、「月末」は翌月の初日マイナスtimedelta(days=1)です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.