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.
I need to increment the month of a datetime value
next_month = datetime.datetime(mydate.year, mydate.month+1, 1)
when the month is 12, it becomes 13 and raises error "month must be in 1..12". (I expected the year would increment)
I wanted to use timedelta, but it doesn't take month argument.
There is relativedelta python package, but i don't want to install it just only for this.
Also there is a solution using strtotime.
time = strtotime(str(mydate));
next_month = date("Y-m-d", strtotime("+1 month", time));
I don't want to convert from datetime to str then to time, and then to datetime; therefore, it's still a library too
Does anyone have any good and simple solution just like using timedelta?
This is short and sweet method to add a month to a date using dateutil's relativedelta.
from datetime import datetime
from dateutil.relativedelta import relativedelta
date_after_month = datetime.today()+ relativedelta(months=1)
print('Today: ',datetime.today().strftime('%d/%m/%Y'))
print('After Month:', date_after_month.strftime('%d/%m/%Y'))
Today: 01/03/2013
After Month: 01/04/2013
A word of warning: relativedelta(months=1) and relativedelta(month=1) have different meanings. Passing month=1 will replace the month in original date to January whereas passing months=1 will add one month to original date.
Note: this will require python-dateutil module. If you are on Linux you need to run this command in the terminal in order to install it.
sudo apt-get update && sudo apt-get install python-dateutil
Explanation : Add month value in python
Edit - based on your comment of dates being needed to be rounded down if there are fewer days in the next month, here is a solution:
import datetime
import calendar
def add_months(sourcedate, months):
month = sourcedate.month - 1 + months
year = sourcedate.year + month // 12
month = month % 12 + 1
day = min(sourcedate.day, calendar.monthrange(year,month)[1])
return datetime.date(year, month, day)
In use:
>>> somedate = datetime.date.today()
>>> somedate
datetime.date(2010, 11, 9)
>>> add_months(somedate,1)
datetime.date(2010, 12, 9)
>>> add_months(somedate,23)
datetime.date(2012, 10, 9)
>>> otherdate = datetime.date(2010,10,31)
>>> add_months(otherdate,1)
datetime.date(2010, 11, 30)
Also, if you're not worried about hours, minutes and seconds you could use date rather than datetime. If you are worried about hours, minutes and seconds you need to modify my code to use datetime and copy hours, minutes and seconds from the source to the result.
Here's my salt :
current = datetime.datetime(mydate.year, mydate.month, 1)
next_month = datetime.datetime(mydate.year + int(mydate.month / 12), ((mydate.month % 12) + 1), 1)
Quick and easy :)
since no one suggested any solution, here is how i solved so far
year, month= divmod(mydate.month+1, 12)
if month == 0:
month = 12
year = year -1
next_month = datetime.datetime(mydate.year + year, month, 1)
Use the monthdelta package, it works just like timedelta but for calendar months rather than days/hours/etc.
Here's an example:
from monthdelta import MonthDelta
def prev_month(date):
"""Back one month and preserve day if possible"""
return date + MonthDelta(-1)
Compare that to the DIY approach:
def prev_month(date):
"""Back one month and preserve day if possible"""
day_of_month = date.day
if day_of_month != 1:
date = date.replace(day=1)
date -= datetime.timedelta(days=1)
while True:
try:
date = date.replace(day=day_of_month)
return date
except ValueError:
day_of_month -= 1
from datetime import timedelta
try:
next = (x.replace(day=1) + timedelta(days=31)).replace(day=x.day)
except ValueError: # January 31 will return last day of February.
next = (x + timedelta(days=31)).replace(day=1) - timedelta(days=1)
If you simply want the first day of the next month:
next = (x.replace(day=1) + timedelta(days=31)).replace(day=1)
To calculate the current, previous and next month:
import datetime
this_month = datetime.date.today().month
last_month = datetime.date.today().month - 1 or 12
next_month = (datetime.date.today().month + 1) % 12 or 12
Perhaps add the number of days in the current month using calendar.monthrange()?
import calendar, datetime
def increment_month(when):
days = calendar.monthrange(when.year, when.month)[1]
return when + datetime.timedelta(days=days)
now = datetime.datetime.now()
print 'It is now %s' % now
print 'In a month, it will be %s' % increment_month(now)
What about this one? (doesn't require any extra libraries)
from datetime import date, timedelta
from calendar import monthrange
today = date.today()
month_later = date(today.year, today.month, monthrange(today.year, today.month)[1]) + timedelta(1)
Simplest solution is to go at the end of the month (we always know that months have at least 28 days) and add enough days to move to the next moth:
>>> from datetime import datetime, timedelta
>>> today = datetime.today()
>>> today
datetime.datetime(2014, 4, 30, 11, 47, 27, 811253)
>>> (today.replace(day=28) + timedelta(days=10)).replace(day=today.day)
datetime.datetime(2014, 5, 30, 11, 47, 27, 811253)
Also works between years:
>>> dec31
datetime.datetime(2015, 12, 31, 11, 47, 27, 811253)
>>> today = dec31
>>> (today.replace(day=28) + timedelta(days=10)).replace(day=today.day)
datetime.datetime(2016, 1, 31, 11, 47, 27, 811253)
Just keep in mind that it is not guaranteed that the next month will have the same day, for example when moving from 31 Jan to 31 Feb it will fail:
>>> today
datetime.datetime(2016, 1, 31, 11, 47, 27, 811253)
>>> (today.replace(day=28) + timedelta(days=10)).replace(day=today.day)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: day is out of range for month
So this is a valid solution if you need to move to the first day of the next month, as you always know that the next month has day 1 (.replace(day=1)). Otherwise, to move to the last available day, you might want to use:
>>> today
datetime.datetime(2016, 1, 31, 11, 47, 27, 811253)
>>> next_month = (today.replace(day=28) + timedelta(days=10))
>>> import calendar
>>> next_month.replace(day=min(today.day,
calendar.monthrange(next_month.year, next_month.month)[1]))
datetime.datetime(2016, 2, 29, 11, 47, 27, 811253)
Similar in ideal to Dave Webb's solution, but without all of that tricky modulo arithmetic:
import datetime, calendar
def increment_month(date):
# Go to first of this month, and add 32 days to get to the next month
next_month = date.replace(day=1) + datetime.timedelta(32)
# Get the day of month that corresponds
day = min(date.day, calendar.monthrange(next_month.year, next_month.month)[1])
return next_month.replace(day=day)
This implementation might have some value for someone who is working with billing.
If you are working with billing, you probably want to get "the same date next month (if possible)" as opposed to "add 1/12 of one year".
What is so confusing about this is you actually need take into account two values if you are doing this continuously. Otherwise for any dates past the 27th, you'll keep losing a few days until you end up at the 27th after leap year.
The values you need to account for:
The value you want to add a month to
The day you started with
This way if you get bumped from the 31st down to the 30th when you add one month, you'll get bumped back up to the 31st for the next month that has that day.
This is how I did it:
def closest_date_next_month(year, month, day):
month = month + 1
if month == 13:
month = 1
year = year + 1
condition = True
while condition:
try:
return datetime.datetime(year, month, day)
except ValueError:
day = day-1
condition = day > 26
raise Exception('Problem getting date next month')
paid_until = closest_date_next_month(
last_paid_until.year,
last_paid_until.month,
original_purchase_date.day) # The trick is here, I'm using the original date, that I started adding from, not the last one
Well with some tweaks and use of timedelta here we go:
from datetime import datetime, timedelta
def inc_date(origin_date):
day = origin_date.day
month = origin_date.month
year = origin_date.year
if origin_date.month == 12:
delta = datetime(year + 1, 1, day) - origin_date
else:
delta = datetime(year, month + 1, day) - origin_date
return origin_date + delta
final_date = inc_date(datetime.today())
print final_date.date()
I was looking to solve the related problem of finding the date for the first of the following month, regardless of the day in the given date. This does not find the same day 1 month later.
So, if all you want is to put in December 12, 2014 (or any day in December) and get back January 1, 2015, try this:
import datetime
def get_next_month(date):
month = (date.month % 12) + 1
year = date.year + (date.month + 1 > 12)
return datetime.datetime(year, month, 1)
A solution without the use of calendar:
def add_month_year(date, years=0, months=0):
year, month = date.year + years, date.month + months + 1
dyear, month = divmod(month - 1, 12)
rdate = datetime.date(year + dyear, month + 1, 1) - datetime.timedelta(1)
return rdate.replace(day = min(rdate.day, date.day))
def add_month(d,n=1): return type(d)(d.year+(d.month+n-1)/12, (d.month+n-1)%12+1, 1)
Just Use This:
import datetime
today = datetime.datetime.today()
nextMonthDatetime = today + datetime.timedelta(days=(today.max.day - today.day)+1)
This is what I came up with
from calendar import monthrange
def same_day_months_after(start_date, months=1):
target_year = start_date.year + ((start_date.month + months) / 12)
target_month = (start_date.month + months) % 12
num_days_target_month = monthrange(target_year, target_month)[1]
return start_date.replace(year=target_year, month=target_month,
day=min(start_date.day, num_days_target_month))
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)
def month_add(year, month, add_month):
return month_sub(year, month, -add_month)
>>> month_add(2015, 7, 1)
(2015, 8)
>>> month_add(2015, 7, 20)
(2017, 3)
>>> month_add(2015, 7, 12)
(2016, 7)
>>> month_add(2015, 7, 24)
(2017, 7)
>>> month_add(2015, 7, -2)
(2015, 5)
>>> month_add(2015, 7, -12)
(2014, 7)
>>> month_add(2015, 7, -13)
(2014, 6)
example using the time object:
start_time = time.gmtime(time.time()) # start now
#increment one month
start_time = time.gmtime(time.mktime([start_time.tm_year, start_time.tm_mon+1, start_time.tm_mday, start_time.tm_hour, start_time.tm_min, start_time.tm_sec, 0, 0, 0]))
My very simple solution, which doesn't require any additional modules:
def addmonth(date):
if date.day < 20:
date2 = date+timedelta(32)
else :
date2 = date+timedelta(25)
date2.replace(date2.year, date2.month, day)
return date2
I'm very new to Python hence this question.
I have a list that represents dates i.e. Mondays in March and beginning of April
[2, 9, 16, 23, 30, 6]
The list, 'color_sack' is created from a scrape of our local council website.
Im using
next_rubbish_day = next(x for x in color_sack if x > todays_date.day)
todays_date.day returns just the number representing the day i.e. 30
This has worked well all month until today 30th when it now displays a error
next_rubbish_day = next(x for x in color_sack if x > todays_date.day)
StopIteration
Is it possible to step through the list a better way so as next_rubbish_day would populate the 6 after the 30 from the list above.
I can see why its not working but can't work out a better way.
When April starts the list will be updated with the new dates for Mondays in April through to the beginning of May
Consider, if your current month is march and corresponding list of dates is [2, 9, 16, 23, 30, 6] and today's date is 30, basically what we are doing is :
Checking if there is any date in color_sack that is greater than
today's date if it is then we yield that date. In our case no date in the list is greater than 30.
If the 1st condition fails we now find out the index of maximum date in the color_sack, in our case the max date is 30 and its index is 4, now we found out if there is a idx greater than the index of maximum date in the list, if it is then we return that date.
This algorithm will comply with any dates in the current month eg March. As soon as the new month starts eg. "April starts the list will be updated with the new dates for Mondays in April through to the beginning of May".
So this algorithm will always comply.
Try this:
def next_rubbish_day(color_sack, todays_date):
for idx, day in enumerate(color_sack):
if day > todays_date or idx > color_sack.index(max(color_sack)):
yield day
print(next(next_rubbish_day(color_sack, 6)))
print(next(next_rubbish_day(color_sack, 10)))
print(next(next_rubbish_day(color_sack, 21)))
print(next(next_rubbish_day(color_sack, 30)))
print(next(next_rubbish_day(color_sack, 31)))
OUTPUT:
9
16
23
6
6
next takes an optional default that is returned when the iterable is empty. If color_sack consistently has the first-of-next-month day in the last position, return it as a default:
next_rubbish_day = next(
(x for x in color_sack[:-1] if x > todays_date.day),
color_sack[-1],
)
Note that this scheme will not tell you whether you rolled over. It will only tell you the next date is 6th, not 6th of April versus 6th of March.
To avoid the magic indices, consider splitting your list explicitly and giving proper names to each part.
*this_month, fallback_day = color_sack
next_rubbish_day = next(
(day for day in this_month if day > todays_date.day),
fallback_day,
)
If you need to be month-aware, handle the StopIteration explicitly:
try:
day = next(x for x in color_sack[:-1] if x > todays_date.day)
except StopIteration:
day = color_sack[-1]
month = 'next'
else:
month = 'this'
print(f'Next date is {day} of {month} month')
Thank you for the help, Ive used MisterMiyagi snippet as that seems to work at the moment.
Here is the full code:
import datetime
import requests
import calendar
from bs4 import BeautifulSoup
from datetime import date
def ord(n): # returns st, nd, rd and th
return str(n) + (
"th" if 4 <= n % 100 <= 20 else {
1: "st", 2: "nd", 3: "rd"}.get(n % 10, "th")
)
# Scrapes rubbish collection dates
URL = "https://apps.castlepoint.gov.uk/cpapps/index.cfm?roadID=2767&fa=wastecalendar.displayDetails"
raw_html = requests.get(URL)
data = BeautifulSoup(raw_html.text, "html.parser")
pink = data.find_all('td', class_='pink', limit=3)
black = data.find_all('td', class_='normal', limit=3)
month = data.find('div', class_='calMonthCurrent')
# converts .text and strip [] to get month name
month = str((month.text).strip('[]'))
todays_date = datetime.date.today()
print()
# creats sack lists
pink_sack = []
for div in pink:
n = div.text
pink_sack.append(n)
pink_sack = list(map(int, pink_sack))
print(f"Pink list {pink_sack}")
black_sack = []
for div in black:
n = div.text
black_sack.append(n)
black_sack = list(map(int, black_sack))
print(f"Black list {black_sack}")
# creats pink/black list
color_sack = []
color_sack = [None]*(len(pink_sack)+len(black_sack))
color_sack[::2] = pink_sack
color_sack[1::2] = black_sack
print(f"Combined list {color_sack}")
print()
print()
# checks today for rubbish
if todays_date.day in color_sack:
print(f"Today {(ord(todays_date.day))}", end=" ")
if todays_date.day in pink_sack:
print("is pink")
elif todays_date.day in black_sack:
print("is black")
# Looks for the next rubbish day
next_rubbish_day = next(
(x for x in color_sack[:-1] if x > todays_date.day),
color_sack[-1],
)
# gets day number
day = calendar.weekday(
(todays_date.year), (todays_date.month), (next_rubbish_day))
# print(next_rubbish_day)
print(f"Next rubbish day is {(calendar.day_name[day])} the {(ord(next_rubbish_day))}" +
(" and is Pink" if next_rubbish_day in pink_sack else " and is Black"))
print()
Theres probable so many more efficient ways of doing this, so Im open to suggestions and always learning.
Hi I'm a beginner at Python and am currently using Python 3.4.1 on PyCharm. I have recently made a project that calculates the amount of days between 2 dates, but there are 2 problems.
def get_first_day():
while True:
try:
print('First Date')
day = int(input('Day:'))
month = int(input('Month:'))
year = int(input('Year:'))
print(day, '/', month, '/', year)
date = [day, month, year * 365]
get_second_day(date)
except ValueError:
print('You were supposed to enter a date.')
def get_second_day(date_1):
while True:
try:
print('Second Date')
day = int(input('Day:'))
month = int(input('Month:'))
year = int(input('Year:'))
print(day, '/', month, '/', year)
date = [day, month, year * 365]
convert_dates_and_months(date_1, date)
except ValueError:
print('You were supposed to enter a date.')
def convert_dates_and_months(date_1, date_2):
days_unfiltered = [date_1[0], date_2[0]]
months_unfiltered = [date_1[1], date_2[1]]
year = [date_1[2], date_2[2]]
date_unfiltered = zip(days_unfiltered, months_unfiltered, year)
for d, m, y in date_unfiltered:
if m in [1, 3, 5, 7, 8, 10, 12]:
a = 31
elif m in [4, 6, 9, 11]:
a = 30
elif m in [2, 0] and int(y) % 4 is 0:
a = 29
else:
a = 28
m *= a
days = list(filter(lambda x: 0 < x < (a + 1), days_unfiltered))
months = list(filter(lambda x: 0 < x < 13, months_unfiltered))
date_1 = [days[0], months[0], year[0]]
date_2 = [days[1], months[1], year[1]]
determine_date_displacement(date_1, date_2)
def determine_date_displacement(date_1, date_2):
full_dates = zip(date_1, date_2)
days = -1
for k, v in full_dates:
days += (int(v) - int(k))
if days < 0:
days *= -1
print(days)
get_first_day()
The first problem is that the counter returns an incorrect number of days between 2 dates. The second is that def get_second_day repeats at the end for some reason. I'll show you what I mean:
First Date
Day:10
Month:09
Year:03
10 / 9 / 3
Second Date
Day:06
Month:06
Year:06
6 / 6 / 6
1087
Second Date
Day:
I know for a fact there are exactly 1,000 days between 10/09/03 and 06/06/06, yet the project returns 1,087 days.
If anyone could explain why this project is returning an incorrect number, as well as why it asks me to fill the second date again at the end, that would be perfect.
As this is my first question and I'm a beginner at Python, I apologise in advance for any weird phrasing/bad practices seen in this question.
Problem 1:
Your leap year calculation is off:
Leap years are years % 4 == 0 but only for years not year % 100 == 0 unless
they are also year % 400 == 0:
2004,2008,2012 : leap year (%4==0, not %100==0)
1700,1800,1900 : no leap year (%4 == 0 , % 100 == 0 but not %400 == 0)
1200,1600,2000 : leap years (* 1200 theor. b/c gregorian cal start)
Problem 2:
In your input you premultiply the year by 365 w/o checking for leap-years - they schould have 366 days but got 365 - which would result in lacking days when computing the amount of days for years that leap(ed).
Problem 3:
You have a controlflow-issue: the get_second_day() repeats because you do:
get_first_date()
while without end:
do smth
call get_second_date(..)
while without end:
do smth
call some calculation functions
that calc and print and return with None
back in get_second_date(), no break, so back to the beginning
of its while and start over forever - you are TRAPPED
fix it by putting a break after convert_dates_and_months(date_1, date) inside get_second_day(..)
Suggestions:
You can streamline the input by reducing the amount of duplicate code between get_first_day() and get_second_day() - this follows the DRY principle (Don't Repeat Yourself):
def getDate(text):
while True:
try:
print(text)
day = int(input('Day:'))
month = int(input('Month:'))
year = int(input('Year:'))
print(day, '/', month, '/', year)
return [day, month, year * 365] # see Problem 2
except ValueError:
print('You were supposed to enter a date.')
def get_first_day():
date1 = getDate("First Date")
# rest of code omitted
def get_second_day(date_1):
date = getDate("Second Date")
# rest of code omitted
A better solution would utilize datetime and datettime-parsing, especially if you want to handle input validation and leap-year estimation you would need far more checks.
Using datetime module would simplyfy this a lot:
import datetime
def getDate(text):
while True:
try:
print(text)
day = int(input('Day:'))
month = int(input('Month:'))
year = int(input('Year (4 digits):'))
print(day, '/', month, '/', year)
# this will throw error on invalid dates:
# f.e. 66.22.2871 or even (29.2.1977) and user
# gets a new chance to input something valid
return datetime.datetime.strptime("{}.{}.{}".format(year,month,day),"%Y.%m.%d")
except (ValueError,EOFError):
print('You were supposed to enter a valid date.')
def get_first_day():
return getDate("First Date")
def get_second_day():
return getDate("Second Date")
# while True: # uncomment and indent next lines to loop endlessly
first = get_first_day() # getDate("First Date") and getDate("Second Date")
second = get_second_day() # directly would be fine IMHO, no function needed
print( (second-first).days)
Output:
First Date
Day:10
Month:9
Year (4 digits):2003
10 / 9 / 2003
Second Date
Day:6
Month:6
Year (4 digits):2006
6 / 6 / 2006
1000
Good read: How to debug small programs (#1) - following it, could have at least lead you to the control-flow issue.
With python I want to calculate the delta days of a day_of_a_year day and its corresponding month, as well delta days for month + 1.
*Sorry I forgot to mention that the year is a known variable
eg.
def a(day_of_year):
<...>
return [(days_from_start_of_month),(days_untill_end_of_month)]
so
If
day_of_year = 32
a(32) = (2,28) #assuming the month which the day_of_year corresponds to starts from day 30 and ends to day 60.
So far im studying the datetime , timeutils and calendar modules and I really can't figure out the logic for the code! I wish i had something solid to show, but Im getting lost somewhere in timedelta functions.
The first day of the month is easy to construct, as is the first day of the next month. Once you have those, the rest is even easier. As pointed out by the OP, the calendar.monthrange function gives us the most readable method to get the last day of a month.
>>> from datetime import date, year
>>> import calendar
>>> def first_day(dt):
... # Simply copy year and month into new date instance
... return date(dt.year, dt.month, 1)
...
>>> def last_day(dt):
... days_in_month = calendar.monthrange(dt.year, dt.month)[1]
... return date(dt.year, dt.month, days_in_month)
...
>>> nth_day = 32
>>> day_of_year = date(2012, 1, 1) + timedelta(days=nth_day - 1)
>>> day_of_year
datetime.date(2012, 2, 1)
>>> first_day(day_of_year), last_day(day_of_year)
(datetime.date(2012, 2, 1), datetime.date(2012, 2, 29))
>>> day_of_year - first_day(day_of_year), last_day(day_of_year) - day_of_year
(datetime.timedelta(0), datetime.timedelta(28))
To combine these techniques into one function:
def delta_to_start_and_end(year, day_of_year):
dt = date(year, 1, 1) + timedelta(days=(day_of_year - 1))
def first_day(dt):
return date(dt.year, dt.month, 1)
def last_day(dt):
days_in_month = calendar.monthrange(dt.year, dt.month)[1]
return date(dt.year, dt.month, days_in_month)
return (dt - first_day(dt)).days, (last_day(dt) - dt).days
Output:
>>> delta_to_start_and_end(2012, 32)
(0, 28)
>>> delta_to_start_and_end(2011, 32)
(0, 27)
>>> delta_to_start_and_end(2012, 34)
(2, 26)
>>> delta_to_start_and_end(2012, 364)
(28, 2)
I'm not sure if you want to add 1 to each of these two values; currently the first day of the month (first example) gives you 0 as the first value and (days-in-the-month - 1) as the second value, as this is the difference in days from those points. It's trivial to add + 1 twice on the last line of the delta_to_start_and_end function if you need these.
As a historic note, a previous version of this answer used a different method to calculate the last day of a month without the calendar module:
def last_day(dt):
rest, month = divmod(dt.month, 12)
return date(dt.year + rest, month + 1, 1) - timedelta(days=1)
This function uses the divmod builtin function to handle the 'current month is December' edge-case; in that case the next month is not 13, but 1 and we'd need to increase the year by one as well. Rolling over a number back to the 'start' is the modulus of the number, but the divmod function gives us the divisor as well, which happens to be 1 if the current month is 12. This gives us a handy indicator when to increase the year.
I don't think that there's an existing library that works for this. You have to make something yourself, like this:
monthdays = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
day = 32
total = 0
for i in monthdays:
if day - total - i < 0:
before = day - total
after = total + i - day
break
total += i
print before, after
(just a quick start, there is possibly a more elegant way)