If only timedelta had a month argument in it's constructor. So what's the simplest way to do this?
EDIT: I wasn't thinking too hard about this as was pointed out below. Really what I wanted was any day in the last month because eventually I'm going to grab the year and month only. So given a datetime object, what's the simplest way to return any datetime object that falls in the previous month?
You can use the third party dateutil module (PyPI entry here).
import datetime
import dateutil.relativedelta
d = datetime.datetime.strptime("2013-03-31", "%Y-%m-%d")
d2 = d - dateutil.relativedelta.relativedelta(months=1)
print d2
output:
2013-02-28 00:00:00
After the original question's edit to "any datetime object in the previous month", you can do it pretty easily by subtracting 1 day from the first of the month.
from datetime import datetime, timedelta
def a_day_in_previous_month(dt):
return dt.replace(day=1) - timedelta(days=1)
Try this:
def monthdelta(date, delta):
m, y = (date.month+delta) % 12, date.year + ((date.month)+delta-1) // 12
if not m: m = 12
d = min(date.day, [31,
29 if y%4==0 and (not y%100==0 or y%400 == 0) else 28,
31,30,31,30,31,31,30,31,30,31][m-1])
return date.replace(day=d,month=m, year=y)
>>> for m in range(-12, 12):
print(monthdelta(datetime.now(), m))
2009-08-06 16:12:27.823000
2009-09-06 16:12:27.855000
2009-10-06 16:12:27.870000
2009-11-06 16:12:27.870000
2009-12-06 16:12:27.870000
2010-01-06 16:12:27.870000
2010-02-06 16:12:27.870000
2010-03-06 16:12:27.886000
2010-04-06 16:12:27.886000
2010-05-06 16:12:27.886000
2010-06-06 16:12:27.886000
2010-07-06 16:12:27.886000
2010-08-06 16:12:27.901000
2010-09-06 16:12:27.901000
2010-10-06 16:12:27.901000
2010-11-06 16:12:27.901000
2010-12-06 16:12:27.901000
2011-01-06 16:12:27.917000
2011-02-06 16:12:27.917000
2011-03-06 16:12:27.917000
2011-04-06 16:12:27.917000
2011-05-06 16:12:27.917000
2011-06-06 16:12:27.933000
2011-07-06 16:12:27.933000
>>> monthdelta(datetime(2010,3,30), -1)
datetime.datetime(2010, 2, 28, 0, 0)
>>> monthdelta(datetime(2008,3,30), -1)
datetime.datetime(2008, 2, 29, 0, 0)
Edit Corrected to handle the day as well.
Edit See also the answer from puzzlement which points out a simpler calculation for d:
d = min(date.day, calendar.monthrange(y, m)[1])
A vectorized, pandas solution is very simple:
df['date'] - pd.DateOffset(months=1)
A variation on Duncan's answer (I don't have sufficient reputation to comment), which uses calendar.monthrange to dramatically simplify the computation of the last day of the month:
import calendar
def monthdelta(date, delta):
m, y = (date.month+delta) % 12, date.year + ((date.month)+delta-1) // 12
if not m: m = 12
d = min(date.day, calendar.monthrange(y, m)[1])
return date.replace(day=d,month=m, year=y)
Info on monthrange from Get Last Day of the Month in Python
I think the simple way is to use DateOffset from Pandas like so:
import pandas as pd
date_1 = pd.to_datetime("2013-03-31", format="%Y-%m-%d") - pd.DateOffset(months=1)
The result will be a Timestamp object
If only timedelta had a month argument
in it's constructor. So what's the
simplest way to do this?
What do you want the result to be when you subtract a month from, say, a date that is March 30? That is the problem with adding or subtracting months: months have different lengths! In some application an exception is appropriate in such cases, in others "the last day of the previous month" is OK to use (but that's truly crazy arithmetic, when subtracting a month then adding a month is not overall a no-operation!), in others yet you'll want to keep in addition to the date some indication about the fact, e.g., "I'm saying Feb 28 but I really would want Feb 30 if it existed", so that adding or subtracting another month to that can set things right again (and the latter obviously requires a custom class holding a data plus s/thing else).
There can be no real solution that is tolerable for all applications, and you have not told us what your specific app's needs are for the semantics of this wretched operation, so there's not much more help that we can provide here.
If all you want is any day in the last month, the simplest thing you can do is subtract the number of days from the current date, which will give you the last day of the previous month.
For instance, starting with any date:
>>> import datetime
>>> today = datetime.date.today()
>>> today
datetime.date(2016, 5, 24)
Subtracting the days of the current date we get:
>>> last_day_previous_month = today - datetime.timedelta(days=today.day)
>>> last_day_previous_month
datetime.date(2016, 4, 30)
This is enough for your simplified need of any day in the last month.
But now that you have it, you can also get any day in the month, including the same day you started with (i.e. more or less the same as subtracting a month):
>>> same_day_last_month = last_day_previous_month.replace(day=today.day)
>>> same_day_last_month
datetime.date(2016, 4, 24)
Of course, you need to be careful with 31st on a 30 day month or the days missing from February (and take care of leap years), but that's also easy to do:
>>> a_date = datetime.date(2016, 3, 31)
>>> last_day_previous_month = a_date - datetime.timedelta(days=a_date.day)
>>> a_date_minus_month = (
... last_day_previous_month.replace(day=a_date.day)
... if a_date.day < last_day_previous_month.day
... else last_day_previous_month
... )
>>> a_date_minus_month
datetime.date(2016, 2, 29)
Returns last day of last month:
>>> import datetime
>>> datetime.datetime.now() - datetime.timedelta(days=datetime.datetime.now().day)
datetime.datetime(2020, 9, 30, 14, 13, 15, 67582)
Returns the same day last month:
>>> x = datetime.datetime.now() - datetime.timedelta(days=datetime.datetime.now().day)
>>> x.replace(day=datetime.datetime.now().day)
datetime.datetime(2020, 9, 7, 14, 22, 14, 362421)
For most use cases, what about
from datetime import date
current_date =date.today()
current_month = current_date.month
last_month = current_month - 1 if current_month != 1 else 12
today_a_month_ago = date(current_date.year, last_month, current_date.day)
That seems the simplest to me.
Note: I've added the second to last line so that it would work if the current month is January as per #Nick's comment
Note 2: In most cases, if the current date is the 31st of a given month the result will be an invalid date as the previous month would not have 31 days (Except for July & August), as noted by #OneCricketeer
I use this for government fiscal years where Q4 starts October 1st. Note I convert the date into quarters and undo it as well.
import pandas as pd
df['Date'] = '1/1/2020'
df['Date'] = pd.to_datetime(df['Date']) #returns 2020-01-01
df['NewDate'] = df.Date - pd.DateOffset(months=3) #returns 2019-10-01 <---- answer
# For fun, change it to FY Quarter '2019Q4'
df['NewDate'] = df['NewDate'].dt.year.astype(str) + 'Q' + df['NewDate'].dt.quarter.astype(str)
# Convert '2019Q4' back to 2019-10-01
df['NewDate'] = pd.to_datetime(df.NewDate)
One liner ?
previous_month_date = (current_date - datetime.timedelta(days=current_date.day+1)).replace(day=current_date.day)
Simplest Way that i have tried Just now
from datetime import datetime
from django.utils import timezone
current = timezone.now()
if current.month == 1:
month = 12
else:
month = current.month - 1
current = datetime(current.year, month, current.day)
Here is some code to do just that. Haven't tried it out myself...
def add_one_month(t):
"""Return a `datetime.date` or `datetime.datetime` (as given) that is
one month earlier.
Note that the resultant day of the month might change if the following
month has fewer days:
>>> add_one_month(datetime.date(2010, 1, 31))
datetime.date(2010, 2, 28)
"""
import datetime
one_day = datetime.timedelta(days=1)
one_month_later = t + one_day
while one_month_later.month == t.month: # advance to start of next month
one_month_later += one_day
target_month = one_month_later.month
while one_month_later.day < t.day: # advance to appropriate day
one_month_later += one_day
if one_month_later.month != target_month: # gone too far
one_month_later -= one_day
break
return one_month_later
def subtract_one_month(t):
"""Return a `datetime.date` or `datetime.datetime` (as given) that is
one month later.
Note that the resultant day of the month might change if the following
month has fewer days:
>>> subtract_one_month(datetime.date(2010, 3, 31))
datetime.date(2010, 2, 28)
"""
import datetime
one_day = datetime.timedelta(days=1)
one_month_earlier = t - one_day
while one_month_earlier.month == t.month or one_month_earlier.day > t.day:
one_month_earlier -= one_day
return one_month_earlier
Given a (year,month) tuple, where month goes from 1-12, try this:
>>> from datetime import datetime
>>> today = datetime.today()
>>> today
datetime.datetime(2010, 8, 6, 10, 15, 21, 310000)
>>> thismonth = today.year, today.month
>>> thismonth
(2010, 8)
>>> lastmonth = lambda (yr,mo): [(y,m+1) for y,m in (divmod((yr*12+mo-2), 12),)][0]
>>> lastmonth(thismonth)
(2010, 7)
>>> lastmonth( (2010,1) )
(2009, 12)
Assumes there are 12 months in every year.
def month_sub(year, month, sub_month):
result_month = 0
result_year = 0
if month > (sub_month % 12):
result_month = month - (sub_month % 12)
result_year = year - (sub_month / 12)
else:
result_month = 12 - (sub_month % 12) + month
result_year = year - (sub_month / 12 + 1)
return (result_year, result_month)
>>> month_sub(2015, 7, 1)
(2015, 6)
>>> month_sub(2015, 7, -1)
(2015, 8)
>>> month_sub(2015, 7, 13)
(2014, 6)
>>> month_sub(2015, 7, -14)
(2016, 9)
I Used the following code to go back n Months from a specific Date:
your_date = datetime.strptime(input_date, "%Y-%m-%d") #to convert date(2016-01-01) to timestamp
start_date=your_date #start from current date
#Calculate Month
for i in range(0,n): #n = number of months you need to go back
start_date=start_date.replace(day=1) #1st day of current month
start_date=start_date-timedelta(days=1) #last day of previous month
#Calculate Day
if(start_date.day>your_date.day):
start_date=start_date.replace(day=your_date.day)
print start_date
For eg:
input date = 28/12/2015
Calculate 6 months previous date.
I) CALCULATE MONTH:
This step will give you the start_date as 30/06/2015.
Note that after the calculate month step you will get the last day of the required month.
II)CALCULATE DAY:
Condition if(start_date.day>your_date.day) checks whether the day from input_date is present in the required month. This handles condition where input date is 31(or 30) and the required month has less than 31(or 30 in case of feb) days. It handles leap year case as well(For Feb). After this step you will get result as 28/06/2015
If this condition is not satisfied, the start_date remains the last date of the previous month. So if you give 31/12/2015 as input date and want 6 months previous date, it will give you 30/06/2015
You can use below given function to get date before/after X month.
from datetime import date
def next_month(given_date, month):
yyyy = int(((given_date.year * 12 + given_date.month) + month)/12)
mm = int(((given_date.year * 12 + given_date.month) + month)%12)
if mm == 0:
yyyy -= 1
mm = 12
return given_date.replace(year=yyyy, month=mm)
if __name__ == "__main__":
today = date.today()
print(today)
for mm in [-12, -1, 0, 1, 2, 12, 20 ]:
next_date = next_month(today, mm)
print(next_date)
I think this answer is quite readable:
def month_delta(dt, delta):
year_delta, month = divmod(dt.month + delta, 12)
if month == 0:
# convert a 0 to december
month = 12
if delta < 0:
# if moving backwards, then it's december of last year
year_delta -= 1
year = dt.year + year_delta
return dt.replace(month=month, year=year)
for delta in range(-20, 21):
print(delta, "->", month_delta(datetime(2011, 1, 1), delta))
-20 -> 2009-05-01 00:00:00
-19 -> 2009-06-01 00:00:00
-18 -> 2009-07-01 00:00:00
-17 -> 2009-08-01 00:00:00
-16 -> 2009-09-01 00:00:00
-15 -> 2009-10-01 00:00:00
-14 -> 2009-11-01 00:00:00
-13 -> 2009-12-01 00:00:00
-12 -> 2010-01-01 00:00:00
-11 -> 2010-02-01 00:00:00
-10 -> 2010-03-01 00:00:00
-9 -> 2010-04-01 00:00:00
-8 -> 2010-05-01 00:00:00
-7 -> 2010-06-01 00:00:00
-6 -> 2010-07-01 00:00:00
-5 -> 2010-08-01 00:00:00
-4 -> 2010-09-01 00:00:00
-3 -> 2010-10-01 00:00:00
-2 -> 2010-11-01 00:00:00
-1 -> 2010-12-01 00:00:00
0 -> 2011-01-01 00:00:00
1 -> 2011-02-01 00:00:00
2 -> 2011-03-01 00:00:00
3 -> 2011-04-01 00:00:00
4 -> 2011-05-01 00:00:00
5 -> 2011-06-01 00:00:00
6 -> 2011-07-01 00:00:00
7 -> 2011-08-01 00:00:00
8 -> 2011-09-01 00:00:00
9 -> 2011-10-01 00:00:00
10 -> 2011-11-01 00:00:00
11 -> 2012-12-01 00:00:00
12 -> 2012-01-01 00:00:00
13 -> 2012-02-01 00:00:00
14 -> 2012-03-01 00:00:00
15 -> 2012-04-01 00:00:00
16 -> 2012-05-01 00:00:00
17 -> 2012-06-01 00:00:00
18 -> 2012-07-01 00:00:00
19 -> 2012-08-01 00:00:00
20 -> 2012-09-01 00:00:00
Some time ago I came across the following algorithm which works very well for incrementing and decrementing months on either a date or datetime.
CAVEAT: This will fail if day is not available in the new month. I use this on date objects where day == 1 always.
Python 3.x:
def increment_month(d, add=1):
return date(d.year+(d.month+add-1)//12, (d.month+add-1) % 12+1, 1)
For Python 2.7 change the //12 to just /12 since integer division is implied.
I recently used this in a defaults file when a script started to get these useful globals:
MONTH_THIS = datetime.date.today()
MONTH_THIS = datetime.date(MONTH_THIS.year, MONTH_THIS.month, 1)
MONTH_1AGO = datetime.date(MONTH_THIS.year+(MONTH_THIS.month-2)//12,
(MONTH_THIS.month-2) % 12+1, 1)
MONTH_2AGO = datetime.date(MONTH_THIS.year+(MONTH_THIS.month-3)//12,
(MONTH_THIS.month-3) % 12+1, 1)
I used the following method to substract "n_months" months to a datetime object:
from datetime import datetime, timedelta
def substract_months(original_date: datetime, n_months:int) -> datetime:
ref_date = original_date
for i in range(0, number_of_months):
ref_date = (ref_date.replace(day=1) - timedelta(days=1)).replace(day=1)
ref_date = ref_date.replace(day=original_date.day)
return ref_date
You can use it as:
print(substract_months(original_date=datetime(2022, 11, 16), number_of_months=2))
which returns:
2022-09-16 00:00:00
import datetime
date_str = '08/01/2018'
format_str = '%d/%m/%Y'
datetime_obj = datetime.datetime.strptime(date_str, format_str)
datetime_obj.replace(month=datetime_obj.month-1)
Simple solution, no need for special libraries.
You could do it in two lines like this:
now = datetime.now()
last_month = datetime(now.year, now.month - 1, now.day)
remember the imports
from datetime import datetime
Related
In the below code , from the month and year, need 1st and last date of the month
for ex month =3 and year= 2022
first_date = 2021-03-01 00:00:00
last_date = 2021-03-31 00:00:00
I tried with calendar but it simply returns (1 ,31)
import calendar
month =3
year= 2022
print( calendar.monthrange(2002, 1))
From monthrange() you will get the first day and the number of days in that month. You can use the datetime method to convert it to date.
month =3
year= 2022
first, last = calendar.monthrange(year, month)
print(first, last)
print(datetime.datetime(year, month, 1))
print(datetime.datetime(year, month, last))
You could write your own function to calculate the last day of the month:
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)
So:
>>> last_day_of_month(datetime.date(2021, 3, 19))
datetime.date(2021, 3, 31)
Similarly, we can use a one-liner for the first date:
(dt.replace(day=1) + datetime.timedelta(days=32)).replace(day=1)
So:
dt = datetime.datetime(2021, 3, 19)
print((dt.replace(day=1) + datetime.timedelta(days=32)).replace(day=1))
>>> 2021-03-01 00:00:00
you can do it using datetime module
import datetime
def get_last_day_of_month(day):
next_month = day.replace(day=28) + datetime.timedelta(days=4)
return next_month - datetime.timedelta(days=next_month.day)
the output will be:
for month in range(1, 13):
print(get_last_day_of_month(datetime.date(2020, month, 1)))
2020-01-31
2020-02-29
2020-03-31
2020-04-30
2020-05-31
2020-06-30
2020-07-31
2020-08-31
2020-09-30
2020-10-31
2020-11-30
2020-12-31
for the first day you can just put the day always 1
Using Python...
How can I select all of the Sundays (or any day for that matter) in a year?
[ '01/03/2010','01/10/2010','01/17/2010','01/24/2010', ...]
These dates represent the Sundays for 2010. This could also apply to any day of the week I suppose.
You can use date from the datetime module to find the first Sunday in a year and then keep adding seven days, generating new Sundays:
from datetime import date, timedelta
def allsundays(year):
d = date(year, 1, 1) # January 1st
d += timedelta(days = 6 - d.weekday()) # First Sunday
while d.year == year:
yield d
d += timedelta(days = 7)
for d in allsundays(2010):
print(d)
Pandas has great functionality for this purpose with its date_range() function.
The result is a pandas DatetimeIndex, but can be converted to a list easily.
import pandas as pd
def allsundays(year):
return pd.date_range(start=str(year), end=str(year+1),
freq='W-SUN').strftime('%m/%d/%Y').tolist()
allsundays(2017)[:5] # First 5 Sundays of 2017
# ['01/01/2017', '01/08/2017', '01/15/2017', '01/22/2017', '01/29/2017']
Using the dateutil module, you could generate the list this way:
#!/usr/bin/env python
import dateutil.relativedelta as relativedelta
import dateutil.rrule as rrule
import datetime
year=2010
before=datetime.datetime(year,1,1)
after=datetime.datetime(year,12,31)
rr = rrule.rrule(rrule.WEEKLY,byweekday=relativedelta.SU,dtstart=before)
print rr.between(before,after,inc=True)
Although finding all Sundays is not too hard to do without dateutil, the module is handy especially if you have more complicated or varied date calculations.
If you are using Debian/Ubuntu, dateutil is provided by the python-dateutil package.
from datetime import date, timedelta
from typing import List
def find_sundays_between(start: date, end: date) -> List[date]:
total_days: int = (end - start).days + 1
sunday: int = 6
all_days = [start + timedelta(days=day) for day in range(total_days)]
return [day for day in all_days if day.weekday() is sunday]
date_start: date = date(2018, 1, 1)
date_end: date = date(2018, 12, 31)
sundays = find_sundays_between(date_start, date_end)
If looking for a more general approach (ie not only Sundays), we can build on sth's answer:
def weeknum(dayname):
if dayname == 'Monday': return 0
if dayname == 'Tuesday': return 1
if dayname == 'Wednesday':return 2
if dayname == 'Thursday': return 3
if dayname == 'Friday': return 4
if dayname == 'Saturday': return 5
if dayname == 'Sunday': return 6
This will translate the name of the day into an int.
Then do:
from datetime import date, timedelta
def alldays(year, whichDayYouWant):
d = date(year, 1, 1)
d += timedelta(days = (weeknum(whichDayYouWant) - d.weekday()) % 7)
while d.year == year:
yield d
d += timedelta(days = 7)
for d in alldays(2020,'Sunday'):
print(d)
Note the presence of % 7 in alldays(). This outputs:
2020-01-05
2020-01-12
2020-01-19
2020-01-26
2020-02-02
2020-02-09
2020-02-16
...
Can also do:
for d in alldays(2020,'Friday'):
print(d)
which will give you:
2020-01-03
2020-01-10
2020-01-17
2020-01-24
2020-01-31
2020-02-07
2020-02-14
...
You can iterate over a calendar for that year.
The below should return all Tuesdays and Thursdays for a given year.
# Returns all Tuesdays and Thursdays of a given year
from datetime import date
import calendar
year = 2016
c = calendar.TextCalendar(calendar.SUNDAY)
for m in range(1,13):
for i in c.itermonthdays(year,m):
if i != 0: #calendar constructs months with leading zeros (days belongng to the previous month)
day = date(year,m,i)
if day.weekday() == 1 or day.weekday() == 3: #if its Tuesday or Thursday
print "%s-%s-%s" % (year,m,i)
import time
from datetime import timedelta, datetime
first_date = '2021-01-01'
final_date = '2021-12-31'
first_date = datetime.strptime(first_date, '%Y-%m-%d')
last_date = datetime.strptime(final_date, '%Y-%m-%d')
week_day = 'Sunday'
dates = [first_date + timedelta(days=x) for x in range((last_date - first_date).days + 1) if (first_date + timedelta(days=x)).weekday() == time.strptime(week_day, '%A').tm_wday]
It will return all Sunday date of given date range.
Here's a complete generator function that builds on the solution from #sth. It includes the crucial fix that was mentioned in his solution's comments.
You can specify the day of week (using Python's indexing with 0=Monday to 6=Sunday), the starting date, and the number of weeks to enumerate.
def get_all_dates_of_day_of_week_in_year(day_of_week, start_year, start_month,
start_day, max_weeks=None):
'''
Generator function to enumerate all calendar dates for a specific day
of the week during one year. For example, all Wednesdays in 2018 are:
1/3/2018, 1/10/2018, 1/17/2018, 1/24/2018, 1/31/2018, 2/7/2018, etc.
Parameters:
----------
day_of_week : int
The day_of_week should be one of these values: 0=Monday, 1=Tuesday,
2=Wednesday, 3=Thursday, 4=Friday, 5=Saturday, 6=Sunday.
start_year : int
start_month : int
start_day : int
The starting date from which to list out all the dates
max_weeks : int or None
If None, then list out all dates for the rest of the year.
Otherwise, end the list after max_weeks number of weeks.
'''
if day_of_week < 0 or day_of_week > 6:
raise ValueError('day_of_week should be in [0, 6]')
date_iter = date(start_year, start_month, start_day)
# First desired day_of_week
date_iter += timedelta(days=(day_of_week - date_iter.weekday() + 7) % 7)
week = 1
while date_iter.year == start_year:
yield date_iter
date_iter += timedelta(days=7)
if max_weeks is not None:
week += 1
if week > max_weeks:
break
Example usage to get all Wednesdays starting on January 1, 2018, for 10 weeks.
import calendar
day_of_week = 2
max_weeks = 10
for d in get_all_dates_of_day_of_week_in_year (day_of_week, 2018, 1, 1, max_weeks):
print "%s, %d/%d/%d" % (calendar.day_name[d.weekday()], d.year, d.month, d.day)
The above code produces:
Wednesday, 2018/1/3
Wednesday, 2018/1/10
Wednesday, 2018/1/17
Wednesday, 2018/1/24
Wednesday, 2018/1/31
Wednesday, 2018/2/7
Wednesday, 2018/2/14
Wednesday, 2018/2/21
Wednesday, 2018/2/28
Wednesday, 2018/3/7
according to #sth answer I like to give you an alternative without a function
from datetime import date, timedelta,datetime
sunndays = list()
year_var = datetime.now() #get current date
year_var = year_var.year #get only the year
d = date(year_var, 1, 1) #get the 01.01 of the current year = 01.01.2020
#now we have to skip 4 days to get to sunday.
#d.weekday is wednesday so it has a value of 2
d += timedelta(days=6 - d.weekday()) # 01.01.2020 + 4 days (6-2=4)
sunndays.append(str(d.strftime('%d-%m-%Y'))) #you need to catch the first sunday
#here you get every other sundays
while d.year == year_var:
d += timedelta(days=7)
sunndays.append(str(d.strftime('%d-%m-%Y')))
print(sunndays) # only for control
if you want every monday for example
#for 2021 the 01.01 is a friday the value is 4
#we need to skip 3 days 7-4 = 3
d += timedelta(days=7 - d.weekday())
according to #sth answer,it will lost the day when 1st is sunday.This will be better:
d = datetime.date(year, month-1, 28)
for _ in range(5):
d = d + datetime.timedelta(days=-d.weekday(), weeks=1)
if d.month!=month:
break
date.append(d)
This question already has answers here:
How do I calculate the date six months from the current date using the datetime Python module?
(47 answers)
Closed 7 years ago.
So I am trying to find a way to increment a datetime object by one month. However, it seems this is not so simple, according to this question.
I was hoping for something like:
import datetime as dt
now = dt.datetime.now()
later = now + dt.timedelta(months=1)
But that doesn't work. I was also hoping to be able to go to the same day (or the closest alternative) in the next month if possible. For example, a datetime object set at January 1st would increment to Feb 1st whereas a datetime object set at February 28th would increment to March 31st as opposed to March 28th or something.
To be clear, February 28th would (typically) map to March 31st because it is the last day of the month, and thus it should go to the last day of the month for the next month. Otherwise it would be a direct link: the increment should go to the day in the next month with the same numbered day.
Is there a simple way to do this in the current release of Python?
Check out from dateutil.relativedelta import *
for adding a specific amount of time to a date, you can continue to use timedelta for the simple stuff i.e.
import datetime
from dateutil.relativedelta import *
use_date = datetime.datetime.now()
use_date = use_date + datetime.timedelta(minutes=+10)
use_date = use_date + datetime.timedelta(hours=+1)
use_date = use_date + datetime.timedelta(days=+1)
use_date = use_date + datetime.timedelta(weeks=+1)
or you can start using relativedelta
use_date = use_date+relativedelta(months=+1)
use_date = use_date+relativedelta(years=+1)
for the last day of next month:
use_date = use_date+relativedelta(months=+1)
use_date = use_date+relativedelta(day=31)
Right now this will provide 29/02/2016
for the penultimate day of next month:
use_date = use_date+relativedelta(months=+1)
use_date = use_date+relativedelta(day=31)
use_date = use_date+relativedelta(days=-1)
last Friday of the next month:
use_date = use_date+relativedelta(months=+1, day=31, weekday=FR(-1))
2nd Tuesday of next month:
new_date = use_date+relativedelta(months=+1, day=1, weekday=TU(2))
As #mrroot5 points out dateutil's rrule functions can be applied, giving you an extra bang for your buck, if you require date occurences.
for example:
Calculating the last day of the month for 9 months from the last day of last month.
Then, calculate the 2nd Tuesday for each of those months.
from dateutil.relativedelta import *
from dateutil.rrule import *
from datetime import datetime
use_date = datetime(2020,11,21)
#Calculate the last day of last month
use_date = use_date+relativedelta(months=-1)
use_date = use_date+relativedelta(day=31)
#Generate a list of the last day for 9 months from the calculated date
x = list(rrule(freq=MONTHLY, count=9, dtstart=use_date, bymonthday=(-1,)))
print("Last day")
for ld in x:
print(ld)
#Generate a list of the 2nd Tuesday in each of the next 9 months from the calculated date
print("\n2nd Tuesday")
x = list(rrule(freq=MONTHLY, count=9, dtstart=use_date, byweekday=TU(2)))
for tuesday in x:
print(tuesday)
Last day
2020-10-31 00:00:00
2020-11-30 00:00:00
2020-12-31 00:00:00
2021-01-31 00:00:00
2021-02-28 00:00:00
2021-03-31 00:00:00
2021-04-30 00:00:00
2021-05-31 00:00:00
2021-06-30 00:00:00
2nd Tuesday
2020-11-10 00:00:00
2020-12-08 00:00:00
2021-01-12 00:00:00
2021-02-09 00:00:00
2021-03-09 00:00:00
2021-04-13 00:00:00
2021-05-11 00:00:00
2021-06-08 00:00:00
2021-07-13 00:00:00
rrule could be used to find the next date occurring on a particular day.
e.g. the next 1st of January occurring on a Monday (Given today is the 4th November 2021)
from dateutil.relativedelta import *
from dateutil.rrule import *
from datetime import *
year = rrule(YEARLY,dtstart=datetime.now(),bymonth=1,bymonthday=1,byweekday=MO)[0].year
year
2024
or the next 5 x 1st of January's occurring on a Monday
years = rrule(YEARLY,dtstart=datetime.now(),bymonth=1,bymonthday=1,byweekday=MO)[0:5]
for i in years:print(i.year)
...
2024
2029
2035
2046
2052
The first Month next Year that starts on a Monday:
>>> month = rrule(YEARLY,dtstart=datetime.date(2023, 1, 1),bymonthday=1,byweekday=MO)[0]
>>> month.strftime('%Y-%m-%d : %B')
'2023-05-01 : May'
If you need the months that start on a Monday between 2 dates:
months = rrule(YEARLY,dtstart=datetime.date(2025, 1, 1),until=datetime.date(2030, 1, 1),bymonthday=1,byweekday=MO)
>>> for m in months:
... print(m.strftime('%Y-%m-%d : %B'))
...
2025-09-01 : September
2025-12-01 : December
2026-06-01 : June
2027-02-01 : February
2027-03-01 : March
2027-11-01 : November
2028-05-01 : May
2029-01-01 : January
2029-10-01 : October
This is by no means an exhaustive list of what is available.
Documentation is available here: https://dateutil.readthedocs.org/en/latest/
Note: This answer shows how to achieve this using only the datetime and calendar standard library (stdlib) modules - which is what was explicitly asked for. The accepted answer shows how to better achieve this with one of the many dedicated non-stdlib libraries. If you can use non-stdlib libraries, by all means do so for these kinds of date/time manipulations!
How about this?
def add_one_month(orig_date):
# advance year and month by one month
new_year = orig_date.year
new_month = orig_date.month + 1
# note: in datetime.date, months go from 1 to 12
if new_month > 12:
new_year += 1
new_month -= 12
new_day = orig_date.day
# while day is out of range for month, reduce by one
while True:
try:
new_date = datetime.date(new_year, new_month, new_day)
except ValueError as e:
new_day -= 1
else:
break
return new_date
EDIT:
Improved version which:
keeps the time information if given a datetime.datetime object
doesn't use try/catch, instead using calendar.monthrange from the calendar module in the stdlib:
import datetime
import calendar
def add_one_month(orig_date):
# advance year and month by one month
new_year = orig_date.year
new_month = orig_date.month + 1
# note: in datetime.date, months go from 1 to 12
if new_month > 12:
new_year += 1
new_month -= 12
last_day_of_month = calendar.monthrange(new_year, new_month)[1]
new_day = min(orig_date.day, last_day_of_month)
return orig_date.replace(year=new_year, month=new_month, day=new_day)
Question: Is there a simple way to do this in the current release of Python?
Answer: There is no simple (direct) way to do this in the current release of Python.
Reference: Please refer to docs.python.org/2/library/datetime.html, section 8.1.2. timedelta Objects. As we may understand from that, we cannot increment month directly since it is not a uniform time unit.
Plus: If you want first day -> first day and last day -> last day mapping you should handle that separately for different months.
>>> now
datetime.datetime(2016, 1, 28, 18, 26, 12, 980861)
>>> later = now.replace(month=now.month+1)
>>> later
datetime.datetime(2016, 2, 28, 18, 26, 12, 980861)
EDIT: Fails on
y = datetime.date(2016, 1, 31); y.replace(month=2) results in ValueError: day is out of range for month
Ther is no simple way to do it, but you can use your own function like answered below.
For example:
date 1 : 1 january 2000
date 2 : 17 november 2006
I want to know how many days there are between date 1 and date 2 in the year 2000, 2001, ..., 2006
so I need something that returns something like this (doesn't matter if it's in a list or something):
2000: 365, 2001: 365, ..., 2006: 320
I've looked for something like this on the internet but that only turned up ways to calculate the number of days/months/years between 2 dates
hm, try something like this:
import datetime, calendar
date1 = datetime.date(year1, month1, day1) # month and day are 1-base
date2 = datetime.date(year2, month2, day2)
days_in_first_year = (datetime.date(year1,12,31)-date1).days
days_in_last_year = (date2 - datetime.date(year2, 1, 1)).days
if year1 != year2:
n_days_list = [days_in_first_year]
for year in range(year1+1, year2): n_days_list.append(365 + (1*calendar.isleap(year)))
n_days_list.append(days_in_last_year)
else: n_days_list = [days_in_first_year + days_in_last_year]
haven't tested this, might be some off-by-one errors; make sure it does what you expect.
edit: correct the boundaries of the range() call, correctly handle year1 == year2
>>> start_date = datetime.datetime(2006, 7, 3)
>>> end_date = datetime.datetime(2012, 12, 21)
>>> years = range(start_date.year, end_date.year + 1)
>>> start, end = start_date, end_date + datetime.timedelta(1)
>>> for year in years:
... year_start = datetime.datetime(year, 1, 1, 0, 0)
... year_end = datetime.datetime(year + 1, 1, 1, 0, 0)
... print(year, min(end, year_end) - max(start, year_start))
...
2006 182 days, 0:00:00
2007 365 days, 0:00:00
2008 366 days, 0:00:00
2009 365 days, 0:00:00
2010 365 days, 0:00:00
2011 365 days, 0:00:00
2012 356 days, 0:00:00
UPDATE: You should probably add a datetime.timedelta(1) to the end date, because otherwise you'd be off with one day at the end. Fixed. But that depends on whether you want to include it or exclude it.
from datetime import date
DATE_END = date(2006, 11, 17)
def get_days(date_start):
return (DATE_END - date_start).days
starting_dates = [
date(2000, 1, 1),
date(2001, 1, 1),
date(2002, 1, 1),
]
print map(get_days, starting_dates)
Use this pseudocode to see if a year is a leap-year or not
if year modulo 400 is 0
then is_leap_year
else if year modulo 100 is 0
then not_leap_year
else if year modulo 4 is 0
then is_leap_year
else
not_leap_year
to create a list of all leap-years and the years that's not.
You can have something simply by doing this:
>>> from datetime import datetime
>>> d1 = datetime.strptime("30 Nov 00", "%d %b %y")
>>> (datetime.now() - d1).days
3907
two parts: build the date ranges as tuples with a start and end date, build a dictionary whose key is the year and the values are the days. I don't need to account for leap year in the calculation because that is automatic in the range calculation
date_ranges=[]
def buildDateRanges(start_year,start_month,start_day, end_year,end_month,end_day):
start_date=datetime.datetime(start_year,start_month,start_day)
end_date=datetime.datetime(end_year,end_month,end_day)
start_year=start_date.year
end_year=end_date.year
date_ranges=[]
if start_year != end_year:
for year in range(start_year,end_year+1):
if year==start_year:
date_ranges.append((start_date,datetime.datetime(start_date.year,12,31)))
elif year==end_year:
date_ranges.append((datetime.datetime(start_year+1,1,1),end_date))
else:
date_ranges.append((start_date,end_date))
return date_ranges
date_ranges=buildDateRanges(2006, 7, 3,2012,12,21)
def years_days(days):
years = days // 365
days = days % 365
return years, days
results={}
for i in range(len(date_ranges)):
start_date=date_ranges[i][0]
end_date=date_ranges[i][1]
days=(end_date-start_date).days
years,days=years_days(days)
year=start_date.year
print(years,days)
while True:
if year==end_date.year:
results[year]=days
else:
results[year]=365
year+=1
if year>end_date.year:
break
print(results)
output:
{2006: 181, 2007: 365.0, 2008: 365.0, 2009: 365.0, 2010: 365.0, 2011: 365.0, 2012: 356.0}
Convert both days into seconds since the epoch (ie. 1 Jan 1970 Midnight)
Subtract.
The do the division to convert seconds into days for the difference.
Using Python...
How can I select all of the Sundays (or any day for that matter) in a year?
[ '01/03/2010','01/10/2010','01/17/2010','01/24/2010', ...]
These dates represent the Sundays for 2010. This could also apply to any day of the week I suppose.
You can use date from the datetime module to find the first Sunday in a year and then keep adding seven days, generating new Sundays:
from datetime import date, timedelta
def allsundays(year):
d = date(year, 1, 1) # January 1st
d += timedelta(days = 6 - d.weekday()) # First Sunday
while d.year == year:
yield d
d += timedelta(days = 7)
for d in allsundays(2010):
print(d)
Pandas has great functionality for this purpose with its date_range() function.
The result is a pandas DatetimeIndex, but can be converted to a list easily.
import pandas as pd
def allsundays(year):
return pd.date_range(start=str(year), end=str(year+1),
freq='W-SUN').strftime('%m/%d/%Y').tolist()
allsundays(2017)[:5] # First 5 Sundays of 2017
# ['01/01/2017', '01/08/2017', '01/15/2017', '01/22/2017', '01/29/2017']
Using the dateutil module, you could generate the list this way:
#!/usr/bin/env python
import dateutil.relativedelta as relativedelta
import dateutil.rrule as rrule
import datetime
year=2010
before=datetime.datetime(year,1,1)
after=datetime.datetime(year,12,31)
rr = rrule.rrule(rrule.WEEKLY,byweekday=relativedelta.SU,dtstart=before)
print rr.between(before,after,inc=True)
Although finding all Sundays is not too hard to do without dateutil, the module is handy especially if you have more complicated or varied date calculations.
If you are using Debian/Ubuntu, dateutil is provided by the python-dateutil package.
from datetime import date, timedelta
from typing import List
def find_sundays_between(start: date, end: date) -> List[date]:
total_days: int = (end - start).days + 1
sunday: int = 6
all_days = [start + timedelta(days=day) for day in range(total_days)]
return [day for day in all_days if day.weekday() is sunday]
date_start: date = date(2018, 1, 1)
date_end: date = date(2018, 12, 31)
sundays = find_sundays_between(date_start, date_end)
If looking for a more general approach (ie not only Sundays), we can build on sth's answer:
def weeknum(dayname):
if dayname == 'Monday': return 0
if dayname == 'Tuesday': return 1
if dayname == 'Wednesday':return 2
if dayname == 'Thursday': return 3
if dayname == 'Friday': return 4
if dayname == 'Saturday': return 5
if dayname == 'Sunday': return 6
This will translate the name of the day into an int.
Then do:
from datetime import date, timedelta
def alldays(year, whichDayYouWant):
d = date(year, 1, 1)
d += timedelta(days = (weeknum(whichDayYouWant) - d.weekday()) % 7)
while d.year == year:
yield d
d += timedelta(days = 7)
for d in alldays(2020,'Sunday'):
print(d)
Note the presence of % 7 in alldays(). This outputs:
2020-01-05
2020-01-12
2020-01-19
2020-01-26
2020-02-02
2020-02-09
2020-02-16
...
Can also do:
for d in alldays(2020,'Friday'):
print(d)
which will give you:
2020-01-03
2020-01-10
2020-01-17
2020-01-24
2020-01-31
2020-02-07
2020-02-14
...
You can iterate over a calendar for that year.
The below should return all Tuesdays and Thursdays for a given year.
# Returns all Tuesdays and Thursdays of a given year
from datetime import date
import calendar
year = 2016
c = calendar.TextCalendar(calendar.SUNDAY)
for m in range(1,13):
for i in c.itermonthdays(year,m):
if i != 0: #calendar constructs months with leading zeros (days belongng to the previous month)
day = date(year,m,i)
if day.weekday() == 1 or day.weekday() == 3: #if its Tuesday or Thursday
print "%s-%s-%s" % (year,m,i)
import time
from datetime import timedelta, datetime
first_date = '2021-01-01'
final_date = '2021-12-31'
first_date = datetime.strptime(first_date, '%Y-%m-%d')
last_date = datetime.strptime(final_date, '%Y-%m-%d')
week_day = 'Sunday'
dates = [first_date + timedelta(days=x) for x in range((last_date - first_date).days + 1) if (first_date + timedelta(days=x)).weekday() == time.strptime(week_day, '%A').tm_wday]
It will return all Sunday date of given date range.
Here's a complete generator function that builds on the solution from #sth. It includes the crucial fix that was mentioned in his solution's comments.
You can specify the day of week (using Python's indexing with 0=Monday to 6=Sunday), the starting date, and the number of weeks to enumerate.
def get_all_dates_of_day_of_week_in_year(day_of_week, start_year, start_month,
start_day, max_weeks=None):
'''
Generator function to enumerate all calendar dates for a specific day
of the week during one year. For example, all Wednesdays in 2018 are:
1/3/2018, 1/10/2018, 1/17/2018, 1/24/2018, 1/31/2018, 2/7/2018, etc.
Parameters:
----------
day_of_week : int
The day_of_week should be one of these values: 0=Monday, 1=Tuesday,
2=Wednesday, 3=Thursday, 4=Friday, 5=Saturday, 6=Sunday.
start_year : int
start_month : int
start_day : int
The starting date from which to list out all the dates
max_weeks : int or None
If None, then list out all dates for the rest of the year.
Otherwise, end the list after max_weeks number of weeks.
'''
if day_of_week < 0 or day_of_week > 6:
raise ValueError('day_of_week should be in [0, 6]')
date_iter = date(start_year, start_month, start_day)
# First desired day_of_week
date_iter += timedelta(days=(day_of_week - date_iter.weekday() + 7) % 7)
week = 1
while date_iter.year == start_year:
yield date_iter
date_iter += timedelta(days=7)
if max_weeks is not None:
week += 1
if week > max_weeks:
break
Example usage to get all Wednesdays starting on January 1, 2018, for 10 weeks.
import calendar
day_of_week = 2
max_weeks = 10
for d in get_all_dates_of_day_of_week_in_year (day_of_week, 2018, 1, 1, max_weeks):
print "%s, %d/%d/%d" % (calendar.day_name[d.weekday()], d.year, d.month, d.day)
The above code produces:
Wednesday, 2018/1/3
Wednesday, 2018/1/10
Wednesday, 2018/1/17
Wednesday, 2018/1/24
Wednesday, 2018/1/31
Wednesday, 2018/2/7
Wednesday, 2018/2/14
Wednesday, 2018/2/21
Wednesday, 2018/2/28
Wednesday, 2018/3/7
according to #sth answer I like to give you an alternative without a function
from datetime import date, timedelta,datetime
sunndays = list()
year_var = datetime.now() #get current date
year_var = year_var.year #get only the year
d = date(year_var, 1, 1) #get the 01.01 of the current year = 01.01.2020
#now we have to skip 4 days to get to sunday.
#d.weekday is wednesday so it has a value of 2
d += timedelta(days=6 - d.weekday()) # 01.01.2020 + 4 days (6-2=4)
sunndays.append(str(d.strftime('%d-%m-%Y'))) #you need to catch the first sunday
#here you get every other sundays
while d.year == year_var:
d += timedelta(days=7)
sunndays.append(str(d.strftime('%d-%m-%Y')))
print(sunndays) # only for control
if you want every monday for example
#for 2021 the 01.01 is a friday the value is 4
#we need to skip 3 days 7-4 = 3
d += timedelta(days=7 - d.weekday())
according to #sth answer,it will lost the day when 1st is sunday.This will be better:
d = datetime.date(year, month-1, 28)
for _ in range(5):
d = d + datetime.timedelta(days=-d.weekday(), weeks=1)
if d.month!=month:
break
date.append(d)