96

Given a particular date, say 2011-07-02, how can I find the date of the next Monday (or any weekday day for that matter) after that date?

ghickman
  • 5,893
  • 9
  • 42
  • 51

13 Answers13

167
import datetime
def next_weekday(d, weekday):
    days_ahead = weekday - d.weekday()
    if days_ahead <= 0: # Target day already happened this week
        days_ahead += 7
    return d + datetime.timedelta(days_ahead)

d = datetime.date(2011, 7, 2)
next_monday = next_weekday(d, 0) # 0 = Monday, 1=Tuesday, 2=Wednesday...
print(next_monday)
phihag
  • 278,196
  • 72
  • 453
  • 469
74

Here's a succinct and generic alternative to the slightly weighty answers above.

def onDay(date, day):
    """
    Returns the date of the next given weekday after
    the given date. For example, the date of next Monday.

    NB: if it IS the day we're looking for, this returns 0.
    consider then doing onDay(foo, day + 1).
    """
    days = (day - date.weekday() + 7) % 7
    return date + datetime.timedelta(days=days)
wjandrea
  • 28,235
  • 9
  • 60
  • 81
user1034533
  • 1,054
  • 9
  • 9
  • 2
    Clean but needs two reminders. 1. This returns same date if the weekdays match for day and date.weekday() 2. day should be in the weekday() format, not isoweekday() – bhtabor Feb 07 '19 at 16:49
  • 10
    Works, but the `+7` is unnecessary when you are taking `%7`. Change it to: `onDay = lambda date, day: date + datetime.timedelta(days=(day-date.weekday())%7)` – Zach Siegel May 15 '19 at 20:33
  • 3
    Also, since Python's `datetime` package has a `date` class (that I personally frequently import), I treat `date` as a keyword and don't name variables `date`. I would change this to: `onDay = lambda dt, day: dt + datetime.timedelta(days=(day-dt.weekday())%7)` to remind myself that the format of the input is a Python `datetime.date` variable. (Also, in Javascript, `date` is a standard type, and you shouldn't name variables `date`.) – Zach Siegel May 15 '19 at 20:37
  • 1
    To address bhtabor's issues: 1. To make sure next week's Monday is returned, even if `date` itself is a Monday, change to `onDay = lambda date, day: date + datetime.timedelta(days=(day-date.weekday()-1)%7+1)`. Less importantly: 2. change `date.weekday()` to `date.isoweekday()` if `day` should be a value 1..7 instead of 0..6 for Monday..Sunday. – ElRudi Jun 15 '20 at 22:08
29

Try

>>> dt = datetime(2011, 7, 2)
>>> dt + timedelta(days=(7 - dt.weekday()))
datetime.datetime(2011, 7, 4, 0, 0)

using, that the next monday is 7 days after the a monday, 6 days after a tuesday, and so on, and also using, that Python's datetime type reports monday as 0, ..., sunday as 6.

Dirk
  • 30,623
  • 8
  • 82
  • 102
  • Interesting that - when I try to find the upcoming Sunday after datetime(2013, 1, 1) with "datetime(2013, 1, 1) + timedelta(days=(7 - datetime(2013, 1, 1).weekday()))", I get "datetime.datetime(2013, 1, 7, 0, 0)", which is a Monday. – fixxxer Jun 11 '13 at 14:28
  • 2
    @fixxxer - As it says in the answer: the code uses a few properties of the `datetime` API in ways, which will only work, if you are looking for mondays. The code provided by `phihag` is more general and can find any upcoming day-of-week. – Dirk Jun 11 '13 at 15:35
  • Do you think using 6 instead of 7 in timedelta will help ? – fixxxer Jun 12 '13 at 08:25
  • @fixxxer: Why don't you simply use the code provided by `phihag`? It can do what you want: `next_weekday(datetime.date(2013,1,1), 6)` is `datetime.date(2013, 1, 6)`, a sunday. My code is "cute" in (ab)using certain properties of python's `datetime` API and answers only the "find the next **monday**" part of the question, but not general enough to answer the "find the next *xxx*day" question. – Dirk Jun 12 '13 at 09:32
  • @fixxxer: to find the next Sunday using "cute" code: `d + timedelta(6 - d.weekday() or 13 - d.weekday())` (it is equivalent to phihag's code if `Sunday == 6` is the last day of the week) – jfs Oct 07 '15 at 17:24
9

This is example of calculations within ring mod 7.

import datetime


def next_day(given_date, weekday):
    day_shift = (weekday - given_date.weekday()) % 7
    return given_date + datetime.timedelta(days=day_shift)

now = datetime.date(2018, 4, 15) # sunday
names = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday',    
         'saturday', 'sunday']
for weekday in range(7):
    print(names[weekday], next_day(now, weekday))

will print:

monday 2018-04-16
tuesday 2018-04-17
wednesday 2018-04-18
thursday 2018-04-19
friday 2018-04-20
saturday 2018-04-21
sunday 2018-04-15

As you see it's correctly give you next monday, tuesday, wednesday, thursday friday and saturday. And it also understood that 2018-04-15 is a sunday and returned current sunday instead of next one.

I'm sure you'll find this answer extremely helpful after 7 years ;-)

featuredpeow
  • 2,061
  • 1
  • 19
  • 18
9

Another alternative uses rrule

from dateutil.rrule import rrule, WEEKLY, MO
from datetime import date

next_monday = rrule(freq=WEEKLY, dtstart=date.today(), byweekday=MO, count=1)[0]

rrule docs: https://dateutil.readthedocs.io/en/stable/rrule.html

  • This was the answer I needed. The `relativedelta` version worked fine until after the time on the given day. E.g. when requesting the next Monday at 11:00 and the start time is Monday at 13:00, `relativedelta` will still return the present day at 11:00—which is in the past. `rrule`, on the other hand, looks for the next instance after `dtstart`. So it will correctly return Monday at 11:00 for a `dtstart` of Monday at 10:00 and then _next_ Monday at 11 for a `dtstart` of Monday at 13:00. – Nick K9 Mar 24 '23 at 17:18
  • Definitely the best answer here, thanks. – quartin Apr 28 '23 at 13:20
7

Another simple elegant solution is to use pandas offsets.

I find it very helpful and robust when playing with dates.

  • If you want the first Sunday just modify the frequency to freq='W-SUN'.
  • If you want a couple of next Sundays, change the offsets.Day(days).
  • Using pandas offsets allow you to ignore holidays, work only with Business Days and more.

You can also apply this method easily on a whole DataFrame using the apply method.

import pandas as pd
import datetime

# 1. Getting the closest monday from a given date
date = datetime.date(2011, 7, 2)
closest_monday = pd.date_range(start=date, end=date + pd.offsets.Day(6), freq="W-MON")[
    0
]

# 2. Adding a 'ClosestMonday' column with the closest monday for each row in
# a pandas df using apply. Requires you to have a 'Date' column in your df
def get_closest_monday(row):
    return pd.date_range(
        start=row.Date, end=row.Date + pd.offsets.Day(6), freq="W-MON"
    )[0]


df = pd.DataFrame([datetime.date(2011, 7, 2)], columns=["Date"])
df["ClosestMonday"] = df.apply(lambda row: get_closest_monday(row), axis=1)
print(df)
tantrix
  • 1,248
  • 12
  • 14
Edan Shalom
  • 71
  • 1
  • 3
  • Yes, `date_range` + `W-MON` + `offsets.Day(6)` is *the* elegant solution to the query "every Monday between fixed date in the past and now", all without manual fiddling with weekdays. Thanks! This answer should have more votes. – ojdo Feb 08 '21 at 11:08
  • `offsets` in this example should be `pd.offsets` – henrycjc Oct 27 '21 at 23:53
5

You can start adding one day to date object and stop when it's monday.

>>> d = datetime.date(2011, 7, 2)
>>> while d.weekday() != 0: #0 for monday
...     d += datetime.timedelta(days=1)
... 
>>> d
datetime.date(2011, 7, 4)
utdemir
  • 26,532
  • 10
  • 62
  • 81
5

dateutil has a special feature for this kind of operation and it's the most elegant way I have ever seen yet.

from datetime import datetime
from dateutil.relativedelta import relativedelta, MO

first_monday_date = (datetime(2011,7,2) + relativedelta(weekday=MO(0))).date()

if you want datetime just

first_monday_date = datetime(2011,7,2) + relativedelta(weekday=MO(0))
Utku Cansever
  • 180
  • 2
  • 16
3
import datetime

d = datetime.date(2011, 7, 2)
while d.weekday() != 0:
    d += datetime.timedelta(1)
Jonathan
  • 13,947
  • 17
  • 94
  • 123
1
weekday = 0 ## Monday
dt = datetime.datetime.now().replace(hour=0, minute=0, second=0) ## or any specific date
days_remaining = (weekday - dt.weekday() - 1) % 7 + 1
next_dt = dt + datetime.timedelta(days_remaining)
saeedgnu
  • 4,110
  • 2
  • 31
  • 48
0

This will give the first next Monday after given date:

import datetime

def get_next_monday(year, month, day):
    date0 = datetime.date(year, month, day)
    next_monday = date0 + datetime.timedelta(7 - date0.weekday() or 7)
    return next_monday

print get_next_monday(2011, 7, 2)
print get_next_monday(2015, 8, 31)
print get_next_monday(2015, 9, 1)

2011-07-04
2015-09-07
2015-09-07

rabin utam
  • 13,930
  • 3
  • 20
  • 15
0

Generally to find any date from day of week from today:


def getDateFromDayOfWeek(dayOfWeek):
    week_days = ["monday", "tuesday", "wednesday",
                 "thursday", "friday", "saturday", "sunday"]
    today = datetime.datetime.today().weekday()
    requiredDay = week_days.index(dayOfWeek)
    if today>requiredDay:
          noOfDays=7-(today-requiredDay)
          print("noDays",noOfDays)
    else:  
          noOfDays = requiredDay-today
          print("noDays",noOfDays)
    requiredDate = datetime.datetime.today()+datetime.timedelta(days=noOfDays)
    return requiredDate

print(getDateFromDayOfWeek('sunday').strftime("%d/%m/%y"))

Gives output in format of Day/Month/Year

AkshayB
  • 55
  • 1
  • 7
-1

via list comprehension?

from datetime import *
[datetime.today()+timedelta(days=x) for x in range(0,7) if (datetime.today()+timedelta(days=x)).weekday() % 7 == 0]

(0 at the end is for next monday, returns current date when run on monday)
jose
  • 1