219

I am trying to get the date of the previous month with python. Here is what i've tried:

str( time.strftime('%Y') ) + str( int(time.strftime('%m'))-1 )

However, this way is bad for 2 reasons: First it returns 20122 for the February of 2012 (instead of 201202) and secondly it will return 0 instead of 12 on January.

I have solved this trouble in bash with

echo $(date -d"3 month ago" "+%G%m%d")

I think that if bash has a built-in way for this purpose, then python, much more equipped, should provide something better than forcing writing one's own script to achieve this goal. Of course i could do something like:

if int(time.strftime('%m')) == 1:
    return '12'
else:
    if int(time.strftime('%m')) < 10:
        return '0'+str(time.strftime('%m')-1)
    else:
        return str(time.strftime('%m') -1)

I have not tested this code and i don't want to use it anyway (unless I can't find any other way:/)

Thanks for your help!

philippe
  • 2,949
  • 4
  • 20
  • 34
  • 2
    related: [What's the simplest way to subtract a month from a date in Python?](http://stackoverflow.com/a/7153449/4279) – jfs Aug 25 '14 at 16:29

19 Answers19

471

datetime and the datetime.timedelta classes are your friend.

  1. find today.
  2. use that to find the first day of this month.
  3. use timedelta to backup a single day, to the last day of the previous month.
  4. print the YYYYMM string you're looking for.

Like this:

import datetime
today = datetime.date.today()
first = today.replace(day=1)
last_month = first - datetime.timedelta(days=1)
print(last_month.strftime("%Y%m"))

201202 is printed.

Pixelbog
  • 236
  • 1
  • 8
bgporter
  • 35,114
  • 8
  • 59
  • 65
  • 38
    you could use `.replace()` method: `datetime.utcnow().replace(day=1) - timedelta(days=1)` – jfs Mar 16 '12 at 06:43
  • 2
    Cool! I missed the replace method. – bgporter Mar 16 '12 at 12:55
  • 1
    You can also chain the `.replace()` function. Do it once to get the last month, then do it again to get the day you want. First: `d = date.today()` then `one_month_ago = (d.replace(day=1) - timedelta(days=1)).replace(day=d.day)` – Thane Plummer Jul 15 '15 at 22:12
  • @J.F.Sebastian You are correct -- thanks for pointing that out. There doesn't seem to be an elegant one-liner for this as a "month" is not a constant time period. You can do something ugly by importing `calendar` and using `calendar.mdays[d.month-1]` and more ugliness in a `min()` function in the 2nd `replace`, but it seems un-Pythonic and doesn't account for corner cases. I've updated my answer below using `try - except` which accounts for all cases, although a I hate using exceptions as part of an algorithm. – Thane Plummer Jul 16 '15 at 18:40
  • see the answer of Ivan, and add: min(date.today().day, last_day_of_previous_month.day) – michel.iamit Feb 20 '16 at 10:10
  • Absolutely brilliant! This is the shortest method that is the safest and all around sexy. Kudos my friend. – MuffintopBikini Mar 03 '17 at 01:57
  • `firstOfThisMonth = datetime.date.today().replace(day=1)` `firstOfLastMonth = (firstOfThisMonth - datetime.timedelta(days=1)).strftime("%Y-%m")+"-01"` – michal Jan 17 '19 at 15:19
  • This does not work well on all edge cases. The `dateutil.relativedata` does. – Artur Barseghyan Oct 31 '22 at 08:51
120

You should use dateutil. With that, you can use relativedelta, it's an improved version of timedelta.

>>> import datetime 
>>> import dateutil.relativedelta
>>> now = datetime.datetime.now()
>>> print now
2012-03-15 12:33:04.281248
>>> print now + dateutil.relativedelta.relativedelta(months=-1)
2012-02-15 12:33:04.281248
Dave Butler
  • 1,646
  • 1
  • 12
  • 18
  • It's not working for the first month of year: >>> IllegalMonthError: bad month number -1; must be 1-12 – mtoloo Jan 16 '14 at 10:50
  • @mtoloo What version of dateutil? I am not having that issue, but I will add some alternative syntax – Dave Butler Jan 23 '14 at 04:35
  • 1
    I prefer this one because while @bgporter has a very nice solution, his one doesn't work as nice for looking up the next month. – Daniel F Oct 03 '15 at 10:31
  • 3
    @mtoloo You've probably mistyped months/month – r_black Mar 09 '16 at 10:00
  • 2
    @r_black Yes You are right. That was my fault. Solution given here is correct and no further check is needed for the first month of the year. – mtoloo May 18 '16 at 13:17
  • I had the same problem with @mtoloo, fixed by substracting (-) relativedelta as months=1 – Ali Yeşilkanat Nov 22 '19 at 07:29
  • 1
    Note that if the previous month's date doesn't exist (e.g. October 31st exists, but September 31st does not exist), then relativedelta will use the next older day (e.g. September 30th) – lobi Aug 27 '20 at 22:06
57
from datetime import date, timedelta

first_day_of_current_month = date.today().replace(day=1)
last_day_of_previous_month = first_day_of_current_month - timedelta(days=1)

print "Previous month:", last_day_of_previous_month.month

Or:

from datetime import date, timedelta

prev = date.today().replace(day=1) - timedelta(days=1)
print prev.month
Ivan Pirog
  • 2,982
  • 1
  • 17
  • 7
  • 1
    part of the solution... to find the day of last month, add something like this: day_previous_month = min(today.day, last_day_of_previous_month.day) to avoid exceeding the number of days. – michel.iamit Feb 20 '16 at 10:09
  • #one line formated to YYYYMM 'from datetime import date, timedelta ANOMES = (date.today().replace(day=1) - timedelta(days=1)).strftime("%Y%m")' – Elton da Mata Jan 12 '22 at 14:05
30

For someone who got here and looking to get both the first and last day of the previous month:

from datetime import date, timedelta

last_day_of_prev_month = date.today().replace(day=1) - timedelta(days=1)

start_day_of_prev_month = date.today().replace(day=1) - timedelta(days=last_day_of_prev_month.day)

# For printing results
print("First day of prev month:", start_day_of_prev_month)
print("Last day of prev month:", last_day_of_prev_month)

Output:

First day of prev month: 2019-02-01
Last day of prev month: 2019-02-28
Robin Carlo Catacutan
  • 13,249
  • 11
  • 52
  • 85
  • 1
    If you want to get the start and end of a month (and more), have a look at the `Calendar` module: https://stackabuse.com/introduction-to-the-python-calendar-module/ – toast38coza Jun 03 '19 at 14:49
  • If we want 2nd previous month start and end ? – gamer Sep 24 '19 at 08:16
  • You are a saviour! I was fiddling with calendar, dateutil with my soggy brain. But this is much better. Thank you! – Onewildgamer Sep 05 '22 at 09:14
11

Simple, one liner:

import datetime as dt
previous_month = (dt.date.today().replace(day=1) - dt.timedelta(days=1)).month
Gil Baggio
  • 13,019
  • 3
  • 48
  • 37
10

Its very easy and simple. Do this

from dateutil.relativedelta import relativedelta
from datetime import datetime

today_date = datetime.today()
print "todays date time: %s" %today_date

one_month_ago = today_date - relativedelta(months=1)
print "one month ago date time: %s" % one_month_ago
print "one month ago date: %s" % one_month_ago.date()

Here is the output: $python2.7 main.py

todays date time: 2016-09-06 02:13:01.937121
one month ago date time: 2016-08-06 02:13:01.937121
one month ago date: 2016-08-06
Mahfuz
  • 1,335
  • 1
  • 13
  • 16
9

Building on bgporter's answer.

def prev_month_range(when = None): 
    """Return (previous month's start date, previous month's end date)."""
    if not when:
        # Default to today.
        when = datetime.datetime.today()
    # Find previous month: https://stackoverflow.com/a/9725093/564514
    # Find today.
    first = datetime.date(day=1, month=when.month, year=when.year)
    # Use that to find the first day of this month.
    prev_month_end = first - datetime.timedelta(days=1)
    prev_month_start = datetime.date(day=1, month= prev_month_end.month, year= prev_month_end.year)
    # Return previous month's start and end dates in YY-MM-DD format.
    return (prev_month_start.strftime('%Y-%m-%d'), prev_month_end.strftime('%Y-%m-%d'))
Community
  • 1
  • 1
paragbaxi
  • 3,965
  • 8
  • 44
  • 58
6

With the Pendulum very complete library, we have the subtract method (and not "subStract"):

import pendulum
today = pendulum.datetime.today()  # 2020, january
lastmonth = today.subtract(months=1)
lastmonth.strftime('%Y%m')
# '201912'

We see that it handles jumping years.

The reverse equivalent is add.

https://pendulum.eustace.io/docs/#addition-and-subtraction

Ehvince
  • 17,274
  • 7
  • 58
  • 79
3
def prev_month(date=datetime.datetime.today()):
    if date.month == 1:
        return date.replace(month=12,year=date.year-1)
    else:
        try:
            return date.replace(month=date.month-1)
        except ValueError:
            return prev_month(date=date.replace(day=date.day-1))
steve
  • 41
  • 1
3

Explicit way:

import datetime
result = (datetime.datetime.today().month - 2) % 12 + 1

The problem is how to transfer month [1, 2, 3, ..., 12] to [12, 1, 2, ..., 11].

Step1: month = month - 1 transfer [1, 2, 3, ..., 12] to [0, 1, 2, ..., 11].

Step2: month = (month - 1) % 12 transfer [0, 1, 2, ..., 11] to [11, 0, 1, ..., 10].

Step3: month = month + 1 transfer [11, 0, 1, ..., 10] to [12, 1, 2, ..., 11].

So, the result is result = (month - 2) % 12 + 1

Yan Gong
  • 31
  • 1
1

Just for fun, a pure math answer using divmod. Pretty inneficient because of the multiplication, could do just as well a simple check on the number of month (if equal to 12, increase year, etc)

year = today.year
month = today.month

nm = list(divmod(year * 12 + month + 1, 12))
if nm[1] == 0:
    nm[1] = 12
    nm[0] -= 1
pm = list(divmod(year * 12 + month - 1, 12))
if pm[1] == 0:
    pm[1] = 12
    pm[0] -= 1

next_month = nm
previous_month = pm
Tiberiu Ichim
  • 641
  • 7
  • 7
1

There is a high level library dateparser that can determine the past date given natural language, and return the corresponding Python datetime object

from dateparser import parse
parse('4 months ago')
wesinat0r
  • 121
  • 8
1

You might have come here because you're working with Jython in NiFi. This is how I ended up implementing it. I deviated a little from this answer by Robin Carlo Catacutan because accessing last_day_of_prev_month.day wasn't possible due to a Jython datatype issue explained here that for some reason seems to exist in NiFi'S Jython but not in vanilla Jython.

from datetime import date, timedelta
import calendar
    
flowFile = session.get()
    
if flowFile != None:

    first_weekday_in_prev_month, num_days_in_prev_month = calendar.monthrange(date.today().year,date.today().month-1)

    last_day_of_prev_month = date.today().replace(day=1) - timedelta(days=1)
    first_day_of_prev_month = date.today().replace(day=1) - timedelta(days=num_days_in_prev_month)
            
    last_day_of_prev_month = str(last_day_of_prev_month)
    first_day_of_prev_month = str(first_day_of_prev_month)
    
    flowFile = session.putAllAttributes(flowFile, {
        "last_day_of_prev_month": last_day_of_prev_month,
        "first_day_of_prev_month": first_day_of_prev_month
    })
    
session.transfer(flowFile, REL_SUCCESS)
DSC
  • 365
  • 3
  • 17
1
from datetime import datetime, timedelta, time, timezone

current_time = datetime.now(timezone.utc)
last_day_previous_month = datetime.combine(current_time.replace(day=1), time.max) - timedelta(days=1)
first_day_previous_month = datetime.combine(last_day_previous_month, time.min).replace(day=1)

Output:

first_day_previous_month: 2022-02-01 00:00:00 
last_day_previous_month: 2022-02-28 23:59:59.999999
user856387
  • 51
  • 3
0

Building off the comment of @J.F. Sebastian, you can chain the replace() function to go back one "month". Since a month is not a constant time period, this solution tries to go back to the same date the previous month, which of course does not work for all months. In such a case, this algorithm defaults to the last day of the prior month.

from datetime import datetime, timedelta

d = datetime(2012, 3, 31) # A problem date as an example

# last day of last month
one_month_ago = (d.replace(day=1) - timedelta(days=1))
try:
    # try to go back to same day last month
    one_month_ago = one_month_ago.replace(day=d.day)
except ValueError:
    pass
print("one_month_ago: {0}".format(one_month_ago))

Output:

one_month_ago: 2012-02-29 00:00:00
Thane Plummer
  • 7,966
  • 3
  • 26
  • 30
0

You can do it as below:

from datetime import datetime, timedelta    
last_month = (datetime.now() - timedelta(days=32)).strftime("%Y%m")
Ali Naderi
  • 245
  • 4
  • 8
0

from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta

#Months(0-12) (1 for Previous month)

#last day of (n) previous month (n=months)
day = datetime(2023, 1, 13)
n = 1
lastDayMonth = ((day - relativedelta(months=n) + relativedelta(day=31)).date());

#First day of previous month (n=months=1)
firstDayMonth = ((day - relativedelta(months=n) + relativedelta(day=1)).date());

print("Last Day of Month - "+ str(lastDayMonth))
print("First Day of Month - "+ str(firstDayMonth))

#Last business day (Friday) of prev (n) month if last day on weekend
lastBusDay = (lastDayMonth - timedelta(max(1,(lastDayMonth.weekday() + 6) % 7 - 3))) if lastDayMonth.weekday() in (5,6) else lastDayMonth

print("Last Business Day of Month - " + str(lastBusinessDay))

print()
SAHIL BHANGE
  • 711
  • 5
  • 5
0

It's more easier to use the timedelta module from datetime.

from datetime import datetime, timedelta
now = datetime.now()
lastmonth_date = now - timedelta(days=now.day)

Now, to get last month's number you can use:

lastmonth = lastmonth_date.strftime("%m")

To get the year,

lastmonth_year = lastmonth_date.strftime("%Y")
Adx
  • 119
  • 2
  • 9
-1
import pandas as pd

lastmonth = int(pd.to_datetime("today").strftime("%Y%m"))-1

print(lastmonth)

202101

from datetime import date, timedelta
YYYYMM = (date.today().replace(day=1)-timedelta(days=1)).strftime("%Y%m")
Elton da Mata
  • 181
  • 1
  • 6