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?
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)
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)
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.
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 ;-)
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
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)
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)
import datetime
d = datetime.date(2011, 7, 2)
while d.weekday() != 0:
d += datetime.timedelta(1)
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))
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)
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
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
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)
Related
I use deltatime to subtract 12 weeks and get the exact date
but from this date I have to recover on Friday. this means that if we subtract 12 weeks I have Monday January 3, I have to make a change to get Friday January 7.
Code exemple :
I use this code to get the initial date :
week_num_period = datetime.today()- timedelta(weeks=12)
week_start_perioddddd = week_num_period.strftime("%Y-%m-%d")
but from that date i need to get the friday of that week !
i tried with
week_start_period = next_day(week_start_perioddddd, 'Friday')
week_start_period
but next_day doesn't apply on str !
could someone help me ?
So you want to get the formatted date for the Friday from the week 12 weeks ago?
Add the timedelta of: 4 (weekday index of Friday) minus the weekday index of the day you calculated.
>>> week_num_period = datetime.today()- timedelta(weeks=12)
>>> week_num_period.date()
datetime.date(2022, 1, 6)
>>> friday = week_num_period + timedelta(days=4 - week_num_period.weekday())
>>> friday.date()
datetime.date(2022, 1, 7)
>>> week_start_period = friday.strftime("%Y-%m-%d")
>>> week_start_period
'2022-01-07'
>>>
I want to print all Thursdays between these date ranges
from datetime import date, timedelta
sdate = date(2015, 1, 7) # start date
edate = date(2015, 12, 31) # end date
What is the best pythonic way to do that?
using your sdate.weekday() # returns int between 0 (mon) and 6 (sun):
sdate = ...
while sdate < edate:
if sdate.weekday() != 3: # not thursday
sdate += timedelta(days=1)
continue
# It is thursday
print(sdate)
sdate += timedelta(days=7) # next week
Compute the number of days till thursday from the start date :
days_to_thursday = (3 - sdate.weekday()) % 7
Compute the number of thursdays inbetween both dates:
week_diff = ((edate - sdate).days - days_to_thursday ) // 7
Get all thursdays in the date range:
thursdays = [sdate + timedelta(days=days_to_thursday + 7 * more_weeks) \
for more_weeks in range(week_diff + 1) ]
Print them if you need:
for t in thursdays:
print(t)
Most people are iterating through every day which is a waste. Also might be helpful to delay calculating the thursdays until you actually need them. For this you could use a generator.
def get_days(start, day_index, end=None):
# set the start as the next valid day
start += timedelta(days=(day_index - start.weekday()) % 7)
week = timedelta(days=7)
while end and start < end or not end:
yield start
start += week
This delays getting the next day until you need it, and allows infinite days if you don't specify and end date.
thursday_generator = get_days(date(2015, 1, 7), 3, date(2015, 12, 31))
print(list(thursday_generator))
"""
[datetime.date(2015, 1, 8), datetime.date(2015, 1, 15), datetime.date(2015, 1, 22), ...]
"""
You can easily dump as strings:
print("\n".join(map(str, thursday_generator)))
"""
2015-01-08
2015-01-15
2015-01-22
...
"""
You can also use f-strings for custom string formatting:
print("\n".join(f"{day:%A %x}" for day in thursday_generator))
"""
Thursday 01/08/15
Thursday 01/15/15
Thursday 01/22/15
...
"""
If you don't specify an end date, it goes on forever.
In [28]: thursday_generator = get_days(date(2015, 1, 7), 3)
...: print(len(list(thursday_generator)))
---------------------------------------------------------------------------
OverflowError Traceback (most recent call last)
<ipython-input-28-b161cdcccc75> in <module>
1 thursday_generator = get_days(date(2015, 1, 7), 3)
----> 2 print(len(list(thursday_generator)))
<ipython-input-16-0691db329606> in get_days(start, day_index, end)
5 while end and start < end or not end:
6 yield start
----> 7 start += week
8
OverflowError: date value out of range
You could try a list comprehension to get the Thursdays between the 2 dates.
This code actually outputs the dates as formatted strings but you can get actual dates by dropping the strftime.
from datetime import date, timedelta
sdate = date(2015, 1, 7) # start date
edate = date(2015, 12, 31) # end date
thursdays = [(sdate+timedelta(days=d)).strftime('%A %Y-%m-%d') for d in range(0, (edate-sdate).days+1)
if (sdate+timedelta(days=d)).weekday() ==3]
print('\n'.join(thursdays))
""" Example output
Thursday 2015-01-08
Thursday 2015-01-15
Thursday 2015-01-22
Thursday 2015-01-29
Thursday 2015-02-05
Thursday 2015-02-12
""""
import datetime
import calendar
def weekday_count(start, end, day):
start_date = datetime.datetime.strptime(start, '%d/%m/%Y')
end_date = datetime.datetime.strptime(end, '%d/%m/%Y')
day_count = []
for i in range((end_date - start_date).days):
if calendar.day_name[(start_date + datetime.timedelta(days=i+1)).weekday()] == day:
print(str(start_date + datetime.timedelta(days=i+1)).split()[0])
weekday_count("01/01/2017", "31/01/2017", "Thursday")
# prints result
# 2017-01-05
# 2017-01-12
# 2017-01-19
# 2017-01-26
Simple solution:
from datetime import date, timedelta
sdate = date(2015, 1, 7) # start date
edate = date(2015, 12, 31) # end date
delta = edate - sdate
for day in range(delta.days + 1):
day_obj = sdate + timedelta(days=day)
if day_obj.weekday() == 3: # Thursday
print(day_obj)
# 2015-01-08
# 2015-01-15
# ...
# 2015-12-24
# 2015-12-31
The most efficient solution:
from datetime import date, timedelta
sdate = date(2015, 1, 7) # start date
edate = date(2015, 12, 31) # end date
day_index = 3 # Thursday
delta = (day_index - sdate.weekday()) % 7
match = sdate + timedelta(days=delta)
while match <= edate: # Change this to `<` to ignore the last one
print(match) # Can be easily converted to a generator with `yield`
match += timedelta(days=7)
# 2015-01-08
# 2015-01-15
# ...
# 2015-12-24
# 2015-12-31
Docs:
.weekday(): https://docs.python.org/3/library/datetime.html#datetime.date.weekday
.timedelta(): https://docs.python.org/3/library/datetime.html#timedelta-objects
% operator on negative numbers: The modulo operation on negative numbers in Python
I am trying to get the week ending date for a list of dates. I found the below code and it works for a traditional Monday thru Sunday week but I need a Sunday thru Saturday week (Sunday being the first day of the week).
from datetime import datetime, timedelta
date_str = '2019-06-16'
date_obj = datetime.strptime(date_str, '%Y-%m-%d')
start_of_week = date_obj - timedelta(days=date_obj.weekday())
end_of_week = start_of_week + timedelta(days=6)
print(start_of_week)
print(end_of_week)
The above code returns:
2019-06-10 00:00:00
2019-06-16 00:00:00
and I need it to return this:
2019-06-16 00:00:00
2019-06-22 00:00:00
So weekday returns 0 for Monday and 6 for Sunday. To change that to 0 for Sunday and 6 for Saturday do
start_of_week = date_obj - timedelta((date_obj.weekday() + 1) % 7)
I think in python, Sunday is the start day of a week, LOL. Code below may help you.
from datetime import datetime, timedelta
# this may be the func you need.
def date_start_end(date):
"""
calc the start and end date for input date. Sunday is the start day for a week.
:param date: any date
:return: st: the start date(Sunday) for this week
:return: end: the end date(Saturday) for this week
"""
weekDate = int(date.strftime('%w'))
st = date + timedelta(-weekDate)
end = date + timedelta(6 - weekDate)
return st, end
date = datetime(2019, 6, 16)
st, end = date_start_end(date)
print(st)
print(end)
>>>2019-06-16 00:00:00
>>>2019-06-22 00:00:00
Adding in my two cents. Datetime also has an isoweekday method that has Monday at 1 and Sunday at 7. Similar to #Deepstop's answer but a bit shorter:
start_of_week = date_sample - timedelta(days=date_sample.isoweekday()%7)
I am writing code to take data from the last year. I want to round up the earlier date like so: If it is July 14 2015, I want data from August 1st 2014-July 14,2015
df = pd.read_csv('MyData.csv')
df['recvd_dttm'] = pd.to_datetime(df['recvd_dttm'])
range_max = datetime.datetime.now()
range_min = range_max - pd.tseries.offsets.DateOffset(years=1)+ pd.tseries.offsets.MonthEnd(1) + pd.tseries.offsets.DateOffset(days=1)
if datetime.datetime.now() == is_month_end:
# take slice with final week of data
df = df[(df['recvd_dttm'] >= range_min) &
(df['recvd_dttm'] <= range_max)]
My problem is that when it is July 31, 2015, my code goes to the end of the next month, essentially cutting out an entire month.
I am trying to make a for loop to fix this problem.
If it is the end of the month:
range_min = range_max - pd.tseries.offsets.DateOffset(years=1)
else:
range_min = range_max - pd.tseries.offsets.DateOffset(years=1)+ pd.tseries.offsets.MonthEnd(1) + pd.tseries.offsets.DateOffset(days=1)
How do I tell python to check for the end of the month? MonthEnd is only an offset function.
We can avoid importing the calendar module with a short function that only leverages datetime.
If tomorrow's month is not the same as today's month, then that means today is the last day of the current month. We can check this programmatically with a short function such as
import datetime
def end_of_month(dt):
todays_month = dt.month
tomorrows_month = (dt + datetime.timedelta(days=1)).month
return tomorrows_month != todays_month
Now, for your specific use case:
now = datetime.datetime.now()
if end_of_month(now):
range_min = range_max - pd.tseries.offsets.DateOffset(years=1)
else:
range_min = range_max - pd.tseries.offsets.DateOffset(years=1) +pd.tseries.offsets.MonthEnd(1) + pd.tseries.offsets.DateOffset(days=1)
I simply would use the monthrange method of calendar module to find last day number of the month:
def check_if_last_day_of_week(date):
import datetime
import calendar
# calendar.monthrange return a tuple (weekday of first day of the
# month, number
# of days in month)
last_day_of_month = calendar.monthrange(date.year, date.month)[1]
# here i check if date is last day of month
if date == datetime.date(date.year, date.month, last_day_of_month):
return True
return False
>>> date = datetime.date(2018, 12, 31)
>>> check_if_last_day_of_week(date)
True
If the next day is a different month, it means it is the last day of a month.
def check_if_last_day_of_month(to_date):
delta = datetime.timedelta(days=1)
next_day = to_date + delta
if to_date.month != next_day.month:
return True
return False
I was using Pandas and I did not want to include another library, so I used this to check whether is the last day of the month and last day of the year:
import pandas as pd
my_date = '31-12-2021'
current_data = pd.to_datetime(my_date, format='%d-%m-%Y')
current_month = current_data.month
current_year = current_data.year
following_day = current_data + pd.DateOffset(1)
tomorrows_month = following_day.month
tomorrows_year = following_day.year
is_last_day_of_month = True if tomorrows_month != current_month else False
is_last_day_of_year = True if tomorrows_year != current_year else False
Here's a pure python approach that also takes into account leap years for february:
# total days in every month during non leap years
M_DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
def isleap(year):
"""Return True for leap years, False for non-leap years."""
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
def days_in_month(year, month):
"""Returns total number of days in a month accounting for leap years."""
return M_DAYS[month] + (month == 2 and isleap(year))
def is_monthend(ref_date):
"""Checks whether a date is also a monthend"""
return ref_date.day == days_in_month(ref_date.year, ref_date.month)
Alright, here's what I did. Found the calendar module that BryanOakley suggested and made this loop. It checks the current day and checks if it is the same as the last day of the month, and chooses the range_min accordingly.
if datetime.datetime.now().day == calendar.monthrange(date.year, date.month)[1]:
range_min = range_max - pd.tseries.offsets.DateOffset(years=1)+ pd.tseries.offsets.DateOffset(days=1)
else:
range_min = range_max - pd.tseries.offsets.DateOffset(years=1)+ pd.tseries.offsets.MonthEnd(1) + pd.tseries.offsets.DateOffset(days=1)
import datetime
def find_curr_month_end_date(curr_date):
if(curr_date.month != 12):
next_month_first_date= curr_date.replace(day=1).replace(month=curr_date.month+1)
else:
next_month_first_date= curr_date.replace(day=1).replace(month=1).replace(year=curr_date.year+1)
curr_month_end_date = next_month_first_date - datetime.timedelta(days=1)
return curr_month_end_date
curr_date = datetime.datetime.today()
# or curr_date = datetime.datetime.strptime("2020-12-16","%Y-%m-%d")
curr_month_end_date =
find_curr_month_end_date(curr_date)
Here is a short function to accomplish this. It requires the dateutil module so that you can do relative date math.
import datetime
from dateutil.relativedelta import relativedelta
def lastyear_period_start(current_date):
last_year = current_date - relativedelta(months=11)
return datetime.date(last_year.year, last_year.month, 1)
It can be utilized like so:
dates = [
datetime.datetime(2010, 2, 27),
datetime.datetime(2011, 2, 27),
datetime.datetime(2012, 2, 27),
datetime.datetime(2013, 2, 27),
datetime.datetime(2014, 2, 27),
datetime.datetime(2010, 7, 27),
datetime.datetime(2011, 7, 27),
datetime.datetime(2012, 7, 27),
datetime.datetime(2013, 7, 27),
datetime.datetime(2014, 7, 27),
datetime.datetime(2015, 7, 14),
datetime.datetime(2015, 7, 31),
datetime.datetime(2011, 2, 28),
datetime.datetime(2012, 2, 29),
datetime.datetime(2013, 2, 28),
]
for d in dates:
print d, lastyear_period_start(d)
This prints that following
2010-02-27 00:00:00 2009-03-01
2011-02-27 00:00:00 2010-03-01
2012-02-27 00:00:00 2011-03-01
2013-02-27 00:00:00 2012-03-01
2014-02-27 00:00:00 2013-03-01
2010-07-27 00:00:00 2009-08-01
2011-07-27 00:00:00 2010-08-01
2012-07-27 00:00:00 2011-08-01
2013-07-27 00:00:00 2012-08-01
2014-07-27 00:00:00 2013-08-01
2015-07-14 00:00:00 2014-08-01
2015-07-31 00:00:00 2014-08-01
2011-02-28 00:00:00 2010-03-01
2012-02-29 00:00:00 2011-03-01
2013-02-28 00:00:00 2012-03-01
In the function we're doing two simple steps
last_year = current_date - relativedelta(months=11)
First we find out what the date was 11 months ago, based on the date passed to the function
return datetime.date(last_year.year, last_year.month, 1)
Then we return the first day of that month.
In the output above you can see this accounts for leap years as well.
How would one go about finding the date of the next Saturday in Python? Preferably using datetime and in the format '2013-05-25'?
>>> from datetime import datetime, timedelta
>>> d = datetime.strptime('2013-05-27', '%Y-%m-%d') # Monday
>>> t = timedelta((12 - d.weekday()) % 7)
>>> d + t
datetime.datetime(2013, 6, 1, 0, 0)
>>> (d + t).strftime('%Y-%m-%d')
'2013-06-01'
I use (12 - d.weekday()) % 7 to compute the delta in days between given day and next Saturday because weekday is between 0 (Monday) and 6 (Sunday), so Saturday is 5. But:
5 and 12 are the same modulo 7 (yes, we have 7 days in a week :-) )
so 12 - d.weekday() is between 6 and 12 where 5 - d.weekday() would be between 5 and -1
so this allows me not to handle the negative case (-1 for Sunday).
Here is a very simple version (no check) for any weekday:
>>> def get_next_weekday(startdate, weekday):
"""
#startdate: given date, in format '2013-05-25'
#weekday: week day as a integer, between 0 (Monday) to 6 (Sunday)
"""
d = datetime.strptime(startdate, '%Y-%m-%d')
t = timedelta((7 + weekday - d.weekday()) % 7)
return (d + t).strftime('%Y-%m-%d')
>>> get_next_weekday('2013-05-27', 5) # 5 = Saturday
'2013-06-01'
I found this pendulum pretty useful. Just one line
In [4]: pendulum.now().next(pendulum.SATURDAY).strftime('%Y-%m-%d')
Out[4]: '2019-04-27'
See below for more details:
In [1]: import pendulum
In [2]: pendulum.now()
Out[2]: DateTime(2019, 4, 24, 17, 28, 13, 776007, tzinfo=Timezone('America/Los_Angeles'))
In [3]: pendulum.now().next(pendulum.SATURDAY)
Out[3]: DateTime(2019, 4, 27, 0, 0, 0, tzinfo=Timezone('America/Los_Angeles'))
In [4]: pendulum.now().next(pendulum.SATURDAY).strftime('%Y-%m-%d')
Out[4]: '2019-04-27'
You need two main packages,
import datetime
import calendar
Once you have those, you can simply get the desired date for the week by the following code,
today = datetime.date.today() #reference point.
saturday = today + datetime.timedelta((calendar.SATURDAY-today.weekday()) % 7 )
saturday
Bonus
Following the content, if you type
saturday.weekday()
It will result 5.
Therefore, you can also use 5 in place of calendar.SATURDAY and you will get same result.
saturday = today + datetime.timedelta((5-today.weekday()) % 7 )
if you just want the date from today (inspired from Emanuelle )
def get_next_weekday(weekday_number):
"""
#weekday: week day as a integer, between 0 (Monday) to 6 (Sunday)
"""
assert 0 <= weekday_number <= 6
today_date = datetime.today()
next_week_day = timedelta((7 + weekday_number - today_date.weekday()) % 7)
return (today_date + next_week_day).strftime('%d/%m/%Y')
Again, based on Emmanuel's example, but making 0-6 conform to your week:
ScheduleShift = -1 # make Saturday end of week
EndofWeekDay = lambda do : do + datetime.timedelta( ( ScheduleShift + (13 - do.weekday() ) %7 ) )
Which can be called with:
EndofWeekDay( datetime.date.today() )
returning a datetime.date object
Just wanted to share a code. With this, you will get a list of dates when Saturday days will be in the next 10 days.
from datetime import datetime, timedelta
target_day = 'Saturday'
now_ = datetime.today().date()
how_many_days = 10
next_date = [now_ + timedelta(days=x) for x in range(how_many_days) if (now_ + timedelta(days=x)).strftime("%A") == target_day]
print(next_date)