Cristian
Cristian

Reputation: 44007

Get the last day of the month

Is there a way using Python's standard library to easily determine (i.e. one function call) the last day of a given month?

If the standard library doesn't support that, does the dateutil package support this?

Upvotes: 924

Views: 765718

Answers (30)

Blair Conrad
Blair Conrad

Reputation: 242110

calendar.monthrange provides this information:

calendar.monthrange(year, month)
    Returns weekday of first day of the month and number of days in month, for the specified year and month.

>>> import calendar
>>> calendar.monthrange(2002, 1)
(calendar.TUESDAY, 31)

>>> calendar.monthrange(2008, 2)  # leap years are handled correctly
(calendar.FRIDAY, 29)

>>> calendar.monthrange(2100, 2)  # years divisible by 100 but not 400 aren't leap years
(calendar.MONDAY, 28)

# the module uses the Georgian calendar extended into the past and
# future, so leap days in the distant past will differ from Julian:
>>> calendar.monthrange(1100, 2)
(calendar.THURSDAY, 28)

# note also that pre-Python 3.12, the REPL renders the weekday
# as a bare integer:
>>> calendar.monthrange(2002, 1)
(1, 31)

so:

calendar.monthrange(year, month)[1]

seems like the simplest way to go.

Upvotes: 1509

stack0114106
stack0114106

Reputation: 8791

This solution uses datetime and timedelta modules.

  1. Capture the month in a separate variable.
  2. Resets the date to starting of the year i.e Jan-1st
  3. #2 + Multiply the input month by 32 so that the timedelta advances to next month
  4. Reset the day to 1st so that next month start date is computed.
  5. #4 minus timedelta(1) gives the last day of current month.

Code:

def lastDay(ip_dt):
    dts = datetime.fromisoformat(ip_dt)
    mth = dts.month
    dts = dts.replace(month=1,day=1)
    dts = dts + timedelta(mth*32) 
    dts = dts.replace(day=1) - timedelta(1) 
    return(dts.strftime("%F"))

for i in range(1,13):
    x=lastDay(f"2024-{i:02d}-05")
    print(x)

Solution:

2024-01-31
2024-02-29
2024-03-31
2024-04-30
2024-05-31
2024-06-30
2024-07-31
2024-08-31
2024-09-30
2024-10-31
2024-11-30
2024-12-31

For lasyDayNextMonth, you can add one more timedelta(32) in the step-3

def lastDayNextMonth(ip_dt):
    dts = datetime.fromisoformat(ip_dt)
    mth = dts.month
    dts = dts.replace(month=1,day=1)
    dts = dts + timedelta(mth*32) + timedelta(32) ## <== Here
    dts = dts.replace(day=1) - timedelta(1) 
    return(dts.strftime("%F"))

Upvotes: 1

jfs
jfs

Reputation: 414865

In Python 3.8 there is the undocumented calendar._monthlen(year, month) function:

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

In python 3.7, it was called calendar.monthlen(year, month) without the _underscore.

It is equivalent to the documented calendar.monthrange(year, month)[1] call.

Upvotes: 24

Vitor Xavier
Vitor Xavier

Reputation: 27

I found this really helpful using Python, to find the number of days of current month:

First you will have to get current month and current year using datetime

current_month = datetime.date.today().month
current_year = datetime.date.today().year

Now we get the number of days of current month using:

days_current_month = calendar.monthrange(current_year, current_month)[1]

If you want to find the number of months of a given month, you just need to apply it for the desirable month/year

For example, if we want to know nº of days of February 2024, we need to do this:

current_month = datetime.date.today().replace(month=2).month
current_year = datetime.date.today().replace(year=2024).year



calendar.monthrange(current_year, current_month)[1]

output: 29

With this method, it's possible to automate the nº of days of a given month based on a certain date/interval

Upvotes: 1

Mr.TK
Mr.TK

Reputation: 1836

I've managed to find interesting solution here. It's possible to get last day of the month providing those relativedelta args: day=31, days=+1 and seconds=-1 (which gives you last second of previous day):

import datetime
from dateutil.relativedelta import relativedelta

day_of_febuary = datetime.datetime(2022, 2, 21)
last_day_of_febuary = day_of_febuary + relativedelta(day=31, days=+1, seconds=-1)
print(last_day_of_febuary)
# Output: 2022-02-28 23:59:59

Upvotes: 4

Asclepius
Asclepius

Reputation: 63526

If you don't mind using Pandas, using Timestamp.days_in_month is probably the simplest:

import pandas as pd

> pd.Timestamp(year=2020, month=2, day=1).days_in_month

29

Upvotes: 1

RCN
RCN

Reputation: 111

This one worked for me:

df['daysinmonths'] = df['your_date_col'].apply(lambda t: pd.Period(t, freq='S').days_in_month)

took reference from: https://stackoverflow.com/a/66403016/16607636

Upvotes: 0

jp0d
jp0d

Reputation: 170

Here is a long (easy to understand) version but takes care of leap years.

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]

Upvotes: 3

ramwin
ramwin

Reputation: 6276

Use datetime-month package.

$ pip install datetime-month
$ python
>>> from month import XMonth
>>> Xmonth(2022, 11).last_date()
datetime.date(2022, 11, 30)

Upvotes: 0

Zach Bateman
Zach Bateman

Reputation: 51

Another option is to use a recursive function.

Is the next day in a different month? If so, then the current day is the last day of the month. If the next day is in the same month, try again using that next day.

from datetime import timedelta

def last_day_of_month(date):
    if date.month != (date + timedelta(days=1)).month:
        return date
    else:
        return last_day_of_month(date + timedelta(days=1))

Upvotes: 0

Amit Pathak
Amit Pathak

Reputation: 1377

My approach:

def get_last_day_of_month(mon: int, year: int) -> str:
    '''
    Returns last day of the month.
    '''

    ### Day 28 falls in every month
    res = datetime(month=mon, year=year, day=28)
    ### Go to next month
    res = res + timedelta(days=4)
    ### Subtract one day from the start of the next month
    res = datetime.strptime(res.strftime('%Y-%m-01'), '%Y-%m-%d') - timedelta(days=1)

    return res.strftime('%Y-%m-%d')
>>> get_last_day_of_month(mon=10, year=2022)
... '2022-10-31'

Upvotes: 3

xor007
xor007

Reputation: 1006

I think this is more readable than some of the other answers:

from datetime import timedelta as td
from datetime import datetime as dt
today = dt.now()
a_day_next_month = dt(today.year, today.month, 27) + td(days=5)
first_day_next_month =  dt(a_day_next_month.year, a_day_next_month.month, 1)
last_day_this_month = first_day_next_month - td(days=1)

Upvotes: 0

augustomen
augustomen

Reputation: 9759

If you don't want to import the calendar module, a simple two-step function can also be:

import datetime

def last_day_of_month(any_day):
    # The day 28 exists in every month. 4 days later, it's always next month
    next_month = any_day.replace(day=28) + datetime.timedelta(days=4)
    # subtracting the number of the current day brings us back one month
    return next_month - datetime.timedelta(days=next_month.day)

Outputs:

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

Upvotes: 298

DanielHefti
DanielHefti

Reputation: 344

If it only matters if today is the last day of the month and the date does not really matter, then I prefer to use the condition below.

The logic is quite simple. If tomorrow is the first day of the next month, then today is the last day of the actual month. Below two examples of an if-else condition.

from datetime import datetime, timedelta

if (datetime.today()+timedelta(days=1)).day == 1:
    print("today is the last day of the month")
else:
    print("today isn't the last day of the month")

If timezone awareness is important.

from datetime import datetime, timedelta
import pytz

set(pytz.all_timezones_set)
tz = pytz.timezone("Europe/Berlin")

dt = datetime.today().astimezone(tz=tz)

if (dt+timedelta(days=1)).day == 1:
    print("today is the last day of the month")
else:
    print("today isn't the last day of the month")

Upvotes: 0

Denis Savenko
Denis Savenko

Reputation: 889

If you need to get the first day of the month with 0:00 time and don't want to import any special library you can write like this

import pytz
from datetime import datetime, timedelta

# get now time with timezone (optional)
now = datetime.now(pytz.UTC)

# get first day on this month, get last day on prev month and after get first day on prev month with min time
fist_day_with_time = datetime.combine((now.replace(day=1) - timedelta(days=1)).replace(day=1), datetime.min.time())

Works fine with February 28/29, December - January, and another problem date.

Upvotes: 0

J.Wei
J.Wei

Reputation: 81

Using dateutil.relativedelta

dt + dateutil.relativedelta.relativedelta(months=1, day=1, days=-1)

months=1 and day=1 would shift dt to the first date of next month, then days=-1 would shift the new date to previous date which is exactly the last date of current month.

Upvotes: 6

That's my way - a function with only two lines:

from dateutil.relativedelta import relativedelta

def last_day_of_month(date):
    return date.replace(day=1) + relativedelta(months=1) - relativedelta(days=1)

Example:

from datetime import date

print(last_day_of_month(date.today()))
>> 2021-09-30

Upvotes: 10

Piero
Piero

Reputation: 1893

To me the easier way is using pandas (two lines solution):

    from datetime import datetime
    import pandas as pd

    firstday_month = datetime(year, month, 1)
    lastday_month = firstday_month + pd.offsets.MonthEnd(1)

Another way to do it is: Taking the first day of the month, then adding one month and discounting one day:

    from datetime import datetime
    import pandas as pd

    firstday_month = datetime(year, month, 1)
    lastday_month = firstday_month + pd.DateOffset(months=1) - pd.DateOffset(days=1)

Upvotes: 12

Vince Spicer
Vince Spicer

Reputation: 4530

This is actually pretty easy with dateutil.relativedelta. day=31 will always always return the last day of the month:

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)

Install dateutil with

pip install python-datetutil

Upvotes: 126

Rakesh Chintha
Rakesh Chintha

Reputation: 705

Considering there are unequal number of days in different months, here is the standard solution that works for every month.

import datetime
ref_date = datetime.today() # or what ever specified date

end_date_of_month = datetime.strptime(datetime.strftime(ref_date + relativedelta(months=1), '%Y-%m-01'),'%Y-%m-%d') + relativedelta(days=-1)

In the above code we are just adding a month to our selected date and then navigating to the first day of that month and then subtracting a day from that date.

Upvotes: 1

BIOHAZARD
BIOHAZARD

Reputation: 2043

How about more simply:

import datetime
now = datetime.datetime.now()
datetime.date(now.year, 1 if now.month==12 else now.month+1, 1) - datetime.timedelta(days=1)

Upvotes: 3

Ayush Lal Shrestha
Ayush Lal Shrestha

Reputation: 183

The easiest & most reliable way I've found so Far is as:

from datetime import datetime
import calendar
days_in_month = calendar.monthrange(2020, 12)[1]
end_dt = datetime(2020, 12, days_in_month)

Upvotes: 7

Eugene Yarmash
Eugene Yarmash

Reputation: 150168

The simplest way is to use datetime and some date math, e.g. subtract a day from the first day of the next month:

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

Alternatively, you could use calendar.monthrange() to get the number of days in a month (taking leap years into account) and update the date accordingly:

import calendar, datetime

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

A quick benchmark shows that the first version is noticeably faster:

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)

Upvotes: 5

John Millikin
John Millikin

Reputation: 200996

EDIT: See @Blair Conrad's answer for a cleaner solution


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

Upvotes: 126

Satish Reddy
Satish Reddy

Reputation: 730

Using dateutil.relativedelta you would get last date of month like this:

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

The idea is to get the first day of the month and use relativedelta to go 1 month ahead and 1 day back so you would get the last day of the month you wanted.

Upvotes: 43

Toby Petty
Toby Petty

Reputation: 4690

This is the simplest solution for me using just the standard datetime library:

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)

Upvotes: 5

kevswanberg
kevswanberg

Reputation: 2119

Here is another answer. No extra packages required.

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

Get the first day of the next month and subtract a day from it.

Upvotes: 8

Jake Boomgaarden
Jake Boomgaarden

Reputation: 3606

you can use relativedelta https://dateutil.readthedocs.io/en/stable/relativedelta.html month_end = <your datetime value within the month> + relativedelta(day=31) that will give you the last day.

Upvotes: 5

Pulkit Bansal
Pulkit Bansal

Reputation: 2073

In the code below 'get_last_day_of_month(dt)' will give you this, with date in string format like '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 )

Upvotes: 0

Johannes Blaschke
Johannes Blaschke

Reputation: 37

Here is a solution based 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)

The next_month lambda finds the tuple representation of the first day of the next month, and rolls over to the next year. The month_end lambda transforms a date (dte) to a tuple, applies next_month and creates a new date. Then the "month's end" is just the next month's first day minus timedelta(days=1).

Upvotes: 0

Related Questions