Wie bekomme ich den letzten Tag des Monats?


632

Gibt es eine Möglichkeit, mithilfe der Python-Standardbibliothek den letzten Tag eines bestimmten Monats einfach zu bestimmen (dh einen Funktionsaufruf)?

Wenn die Standardbibliothek dies nicht unterstützt, unterstützt das dateutil-Paket dies?

Antworten:


1082

Ich habe dies früher nicht bemerkt, als ich mir die Dokumentation für das calendarModul angesehen habe , aber eine Methode namens ruft monthrangediese Informationen auf:

monthrange (Jahr, Monat)
    Gibt den Wochentag des ersten Tages des Monats und die Anzahl der Tage im Monat für das angegebene Jahr und den angegebenen Monat zurück.

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

damit:

calendar.monthrange(year, month)[1]

scheint der einfachste Weg zu sein.

Um ganz klar zu sein, monthrangeunterstützt auch Schaltjahre:

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

Meine vorherige Antwort funktioniert immer noch, ist aber eindeutig nicht optimal.


Dies ist keine korrekte Antwort auf die ursprüngliche Frage! Was ist, wenn der letzte Tag des Monats ein Sa / So ist?
Mahdi

8
@ Mahdi: Es ist richtig, die zweite Zahl ist die "Anzahl der Tage im Monat" == "der letzte Tag", unabhängig davon, um welche Art von Tag es sich handelt.
RickyA

Es ist der falsche Tag für das Jahr 1800 im Februar. Ich verstehe es nicht
Schwert 1.

8
@ Schwert1st, ich sehe 28als die Antwort, die richtig ist, unter Verwendung der Regeln für den Gregorianischen Kalender
Blair Conrad

Ich kann nicht der einzige sein, der denkt, es monthrangesei ein verwirrender Name. Sie würden denken, es würde zurückkehren (first_day, last_day), nicht(week_day_of_first_day, number_of_days)
Flimm

146

Wenn Sie das calendarModul nicht importieren möchten , kann eine einfache zweistufige Funktion auch sein:

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)

Ausgänge:

>>> 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

BEARBEITEN: Eine sauberere Lösung finden Sie in der Antwort von @Blair Conrad


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

43
Ich würde diesen Reiniger tatsächlich als Reiniger bezeichnen, außer dass er im Dezember ausfällt, wenn today.month + 1 == 13und Sie einen bekommen ValueError.
Fletom

4
Sie können das lösen, indem Sie verwenden, (today.month % 12) + 1da 12 % 12gibt 0
Bluesmoon

Ein seltsamer Sommerzeitschalter an einem 31. eines Monats (ich denke, das gibt es heute nicht, aber die Zukunft könnte anders sein) könnte den 31. dieses Monats mit einer Länge von nur 23 Stunden rendern, sodass Sie einen Tag subtrahieren können endet am 30. um 23:00:00 Uhr. Das ist sicher ein verrückter Fall, aber es zeigt, dass der Ansatz nicht richtig ist.
Alfe

50

Dies ist eigentlich ziemlich einfach mit dateutil.relativedelta(Paket Python-Datetutil für Pip). day=31wird immer immer den letzten Tag des Monats zurückgeben.

Beispiel:

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
Ich persönlich mag relativedelta (Monate = + 1, Sekunden = -1) scheint offensichtlicher, was los ist
BenH

Du liegst falsch. datetime(2014, 2, 1) + relativedelta(days=31)gibt datetime(2014, 3, 4, 0, 0)...
Emmanuel

9
Sie haben Tage = anstelle von Tag =
Vince Spicer

@BenH Das basiert auf der Hoffnung (ich bin nicht sicher, ob es angegeben ist), dass zuerst das monthshinzugefügt, dann das secondssubtrahiert wird. Da sie bestanden werden, **kwargswürde ich mich nicht darauf verlassen und eine Reihe von getrennten Operationen durchführen: now + relative(months=+1) + relative(day=1) + relative(days=-1)was eindeutig ist.
Alfe

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

44

EDIT: siehe meine andere Antwort . Es hat eine bessere Implementierung als diese, die ich hier lasse, nur für den Fall, dass jemand daran interessiert ist, wie man seinen eigenen Taschenrechner "rollen" kann.

@ John Millikin gibt eine gute Antwort mit der zusätzlichen Schwierigkeit, den ersten Tag des nächsten Monats zu berechnen.

Folgendes ist nicht besonders elegant, aber um den letzten Tag des Monats herauszufinden, in dem ein bestimmtes Datum lebt, können Sie versuchen:

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)

Es klingt albern, aber wie bekomme ich den ersten Tag des Monats ähnlich?
Kishan

10
Ist es nicht immer 1?
Blair Conrad

Ja, aber ich war verwirrt, dass ich ungefähr so ​​aussah: start_date = date (datetime.now (). Year, datetime.now (). Month, 1)
Kishan

4
Ah. today = datetime.date.today(); start_date = today.replace(day=1). Sie sollten vermeiden, datetime.now zweimal anzurufen, falls Sie es am 31. Dezember kurz vor Mitternacht und dann kurz nach Mitternacht angerufen haben. Sie erhalten den 01.01.2016 anstelle des 01.12.2016 oder des 01.01.2017.
Blair Conrad

Dies ist sehr einfach zu verstehen und gibt eine datetime-Instanz zurück, was in vielen Fällen nützlich sein kann. Ein weiterer Vorteil ist , dass es funktioniert , wenn der Eingang dateist eine Instanz datetime.date, datetime.datetimeund auch pandas.Timestamp.
Guilherme Salomé

26

Wenn dateutil.relativedeltaSie verwenden, erhalten Sie das letzte Datum des Monats wie folgt:

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

Die Idee ist, den ersten Tag des Monats zu erhalten und relativedelta1 Monat voraus und 1 Tag zurück zu gehen, damit Sie den letzten Tag des gewünschten Monats erhalten.


13

Eine andere Lösung wäre, so etwas zu tun:

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

Und nutzen Sie die Funktion wie folgt:

>>> 
>>> 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
Zu komplex, bricht die dritte Regel des Zen von Python.
Markuz

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

Dies ist, woraus Fehler gemacht sind;) Versuchen Sie es mit dem 31. Januar
LeartS

@LeartS: es funktioniert bei mir. Was passiert, wenn Sie es versuchen?
Collin Anderson

1
Es klappt. any_day ist der 31. Januar, wir ersetzen den Tag durch 1, also 1. Januar, addiere 32 Tage, wir erhalten den 2. Februar, ersetzen wieder durch Tag = 1 und wir erhalten den 1. Februar. Subtrahiere einen Tag und wir erhalten den 31. Januar. Ich verstehe nicht Was ist das Problem. Welchen Tag bekommst du?
Collin Anderson

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

Wenn Sie bereit sind, eine externe Bibliothek zu verwenden, besuchen Sie http://crsmithdev.com/arrow/

Sie können dann den letzten Tag des Monats erhalten mit:

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

Dies gibt ein Datumsobjekt zurück, das Sie dann bearbeiten können.


9

Um das letzte Datum des Monats zu erhalten, machen wir ungefähr so:

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

Um zu erklären, was wir hier tun, werden wir es in zwei Teile aufteilen:

Zunächst wird die Anzahl der Tage des aktuellen Monats ermittelt, für die wir monthrange verwenden. Blair Conrad hat seine Lösung bereits erwähnt:

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

zweite ist immer das letzte Datum selbst , die wir mit Hilfe tun ersetzen zB

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

und wenn wir sie wie oben erwähnt kombinieren, erhalten wir eine dynamische Lösung.


Obwohl dieser Code zur Lösung des Problems beitragen kann, erklärt er nicht, warum und / oder wie er die Frage beantwortet. Die Bereitstellung dieses zusätzlichen Kontextes würde den langfristigen Bildungswert erheblich verbessern. Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, einschließlich der Einschränkungen und Annahmen.
Toby Speight

Dies scheint am einfachsten zu sein. Wenn Sie zwei Zeilen geben möchten, können Sie eine schöne bekommen, date.replace(day=day)damit jeder weiß, was los ist.
Adam Starrh

9

In Python 3.7 gibt es die undokumentierte calendar.monthlen(year, month)Funktion :

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

Dies entspricht dem dokumentierten calendar.monthrange(year, month)[1]Anruf .


1
Dies scheint unter Python 3.8.1 nicht zu funktionieren: AttributeError: Das Modul 'calendar' hat kein Attribut 'monthlen'
jeppoo1

1
@ jeppoo1: Ja, es ist in bpo-28292 als privat markiert - erwartet für eine undokumentierte Funktion.
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

Benutze Pandas!

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
Sie können auch mit pd.series.dt.is_month_end Link
Pablo

1
Pandas datetime Objekt hat eine spezielle Methode dafür:now=datetime.datetime.now(); pd_now = pd.to_datetime(now); print(pd_now.days_in_month)
Roei Bahumi

3

Für mich ist es der einfachste Weg:

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

Der einfachste Weg (ohne Kalender importieren zu müssen) besteht darin, den ersten Tag des nächsten Monats abzurufen und dann einen Tag davon abzuziehen.

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

Ausgabe:

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

PS: Dieser Code läuft im Vergleich zum import calendarAnsatz schneller . siehe unten:

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)

AUSGABE:

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

In diesem Code wird davon ausgegangen, dass Sie das Datum des letzten Tages des Monats möchten (dh nicht nur den TT-Teil, sondern das gesamte JJJJMMTT-Datum).


warum würden Sie nicht wollen import calendar?
Adam Venturella

1
Weil es schneller ist. Ich habe meine Antwort oben geändert, um dies einzuschließen.
Vishal

@Vishal Sie haben das Konzept richtig verstanden, aber die folgende Zeile war nicht: `` `dt.datetime (thisDate.year, (thisDate + relativedelta (Monate = 1)). Month, 1) - dt.timedelta (Tage = 1)` `` besonders wenn der Monat am Ende des Jahres ist. versuchen Sie stattdessen `` `last_date_of_month = \ first_date_of_month + relativedelta (Monate = 1) - relativedelta (Tage = 1)` ``
XValidated

3

Hier ist eine andere Antwort. Keine zusätzlichen Pakete erforderlich.

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

Holen Sie sich den ersten Tag des nächsten Monats und subtrahieren Sie einen Tag davon.


2

Sie können das Enddatum selbst berechnen. Die einfache Logik besteht darin, einen Tag vom Startdatum des nächsten Monats zu subtrahieren. :) :)

Schreiben Sie also eine benutzerdefinierte Methode.

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

Berufung,

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

Das Enddatum dieses Monats wird zurückgegeben. Übergeben Sie ein beliebiges Datum an diese Funktion. gibt Ihnen das Enddatum dieses Monats zurück.



1

Dies ist für mich die einfachste Lösung, wenn ich nur die Standard-Datetime-Bibliothek verwende:

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

Dies spricht nicht die Hauptfrage an, aber ein netter Trick, um den letzten Wochentag in einem Monat zu erhalten, ist die Verwendung calendar.monthcalendar, die eine Datumsmatrix zurückgibt, die mit Montag als erster Spalte bis Sonntag als letzter Spalte organisiert ist.

# 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

Das Ganze [0:-2] Ganze ist, die Wochenendsäulen zu rasieren und wegzuwerfen. Daten, die außerhalb des Monats liegen, werden mit 0 angezeigt, sodass das Maximum sie effektiv ignoriert.

Die Verwendung von numpy.ravelist nicht unbedingt erforderlich, aber ich hasse es, mich auf die bloße Konvention zu verlassen , numpy.ndarray.maxdie das Array abflacht, wenn nicht angegeben wird, über welche Achse berechnet werden soll.


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

Ausgabe:

31



Dies gibt den letzten Tag des aktuellen Monats aus. In diesem Beispiel war es der 15. Mai 2016. Ihre Ausgabe kann also unterschiedlich sein, die Ausgabe beträgt jedoch so viele Tage wie der aktuelle Monat. Großartig, wenn Sie den letzten Tag des Monats überprüfen möchten, indem Sie einen täglichen Cron-Job ausführen.

Damit:

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

Ausgabe:

False

Es sei denn, es ist der letzte Tag des Monats.


0

Ich bevorzuge diesen Weg

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

Wenn Sie Ihre eigene kleine Funktion erstellen möchten, ist dies ein guter Ausgangspunkt:

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

Dazu muss man die Regeln für die Schaltjahre kennen:

  • jedes vierte Jahr
  • mit Ausnahme von jeweils 100 Jahren
  • aber wieder alle 400 Jahre

1
Sicher, aber die Systembibliotheken verfügen bereits über diese Daten, und sollten die Regeln per Dekret geändert werden, ist die Aktualisierung der Bibliotheken das Problem eines anderen.
Jasen

Gut möglich, aber höchst unwahrscheinlich, und selbst wenn - es würde Sie erst in ungefähr 2000 Jahren beißen ... en.wikipedia.org/wiki/Gregorian_calendar#Accuracy
mathause

Diese Regeln funktionieren seit 500 Jahren nicht mehr. Ich habe kein Vertrauen, dass sie für Tausende von Jahren in der Zukunft gelten werden.
Jasen

0

Wenn Sie einen Datumsbereich übergeben, können Sie Folgendes verwenden:

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

Im folgenden Code gibt 'get_last_day_of_month (dt)' dies mit einem Datum im Zeichenfolgenformat wie 'JJJJ-MM-TT' an.

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

Am einfachsten ist es, datetimeeine Datumsberechnung zu verwenden, z. B. einen Tag vom ersten Tag des nächsten Monats abzuziehen:

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)
    )

Alternativ können Sie calendar.monthrange()die Anzahl der Tage in einem Monat ermitteln (unter Berücksichtigung der Schaltjahre) und das Datum entsprechend aktualisieren:

import calendar, datetime

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

Ein schneller Benchmark zeigt, dass die erste Version spürbar schneller ist:

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

Hier ist eine lange (leicht verständliche) Version, die sich jedoch um Schaltjahre kümmert.

Prost, 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]

Sie können löschen else: passund auch if year % 400 == 0: leap_year_flag = 1mit geringfügigen Änderungen loswerden
canbax

-1

Hier ist eine lösungsbasierte Python Lambdas:

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)

Das next_monthLambda findet die Tupeldarstellung des ersten Tages des nächsten Monats und wechselt zum nächsten Jahr. Das month_endLambda wandelt ein Datum ( dte) in ein Tupel um, wendet es an next_monthund erstellt ein neues Datum. Dann ist das "Monatsende" nur der erste Tag des nächsten Monats minus timedelta(days=1).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.