finding the year and quarter from a datetime object in python - python

I have a requirement to find the completed quarter(1 to 4 from Jan to Dec), given a certain date. Note that it is not the current quarter but the completed quarter. By that I mean that if 1st Jan is the input date, then the completed quarter is 4 and not 1. The small code snippet that I have is as follows:
>>> import datetime
>>> import math
>>> now = datetime.datetime(2015, 1, 1, 0, 0, 0, 0)
>>> present_quarter = int(math.ceil(now.month/3.))
>>> completed_quarter = (present_quarter - 1) if present_quarter != 1 else 4
>>> completed_quarter
4
However what I need is the year as well so that the output is :
2014-4
What is the best way to do this ? Can the output remain a datetime object and not a string by which I mean does Python support a datetime object like Year-Quarter

You could simply map the month to the quarter and a year delta (you need last year for Q4):
def completed_quarter(dt):
prev_quarter_map = ((4, -1), (1, 0), (2, 0), (3, 0))
quarter, yd = prev_quarter_map[(dt.month - 1) // 3]
return (dt.year + yd, quarter)
This translates the month to a value between 0 and 3, then translates that into a quarter number and a year delta. Adding the year delta to the current year gives you the correct year for that completed previous quarter.
Demo:
>>> def completed_quarter(dt):
... prev_quarter_map = ((4, -1), (1, 0), (2, 0), (3, 0))
... quarter, yd = prev_quarter_map[(dt.month - 1) // 3]
... return (dt.year + yd, quarter)
...
>>> from datetime import date
>>> completed_quarter(date(2015, 1, 12))
(2014, 4)
>>> completed_quarter(date(2015, 8, 31))
(2015, 2)
>>> completed_quarter(date(2015, 12, 31))
(2015, 3)
The datetime module has no objects that can model a quarter; you can have a date (which has to have valid year, month and day values) or a date and time (adding on a valid hour, minute, second and microsecond value, with optional timezone).

You just need to catch the first quarter:
def qrt(now):
first_qrt = now.month < 4
return now.year - first_qrt, (now.month - 1) // 3 if not first_qrt else 4

How about extend datetime
class Quarter(datetime):
def __init__(self, *args, **kwargs):
super(Quarter, self).__init__(*args, **kwargs)
present_quarter = int(math.ceil(self.month/3))
self.quarter = (present_quarter-1) if present_quarter != 1 else 4
Using it.
q = Quarter(2015,1,1,0,0,0)
# Or
q = Quarter.now()
# Getting quarter
print q.quarter

Related

Get Dates for Last Week in Python

Ok I'm trying to get the dates for the previous week in Python assuming the week starts on Sunday and Ends on Saturday.
Through google and trial and error, I have this:
def get_week_days(year, week):
d = date(year,1,1)
if(d.weekday()>3):
d = d+timedelta(7-d.weekday())
else:
d = d - timedelta(d.weekday())
dlt = timedelta(days = (week-1)*7)
FH_start = d + dlt
FH_end = d + dlt + timedelta(days=6)
return FH_start, FH_end
print get_week_days(2018, this_week_int)
and that outputs:
(datetime.date(2018, 3, 5), datetime.date(2018, 3, 11))
How do I offset it to have the week start on Sunday, or is there a better way to write this?
Here's one simple version:
from datetime import date, timedelta
def get_week_days(year, week):
year_start = date(year, 1, 1)
# the following line assumes your this_week_int starts at 0
# if this_week_int starts at 1, change to week-2
week_start = year_start + timedelta(days=-year_start.isoweekday(), weeks=week-1)
week_end = week_start + timedelta(days=6)
return week_start, week_end
get_week_days(2018, 1)
# (datetime.date(2017, 12, 31), datetime.date(2018, 1, 6))
get_week_days(2018, 10)
# (datetime.date(2018, 3, 4), datetime.date(2018, 3, 10))
Essentially, .isoweekday() returns 1 for Monday and 7 for Sundays, so when you deduct that weekday it gives you the start at Sunday.
you can go as follows:
from datetime import timedelta
def week_range(date):
year, week, dow = date.isocalendar()
# Find the first day of the week.
if dow == 7:
# Since we want to start with Sunday, let's test for that condition.
start_date = date
else:
# Otherwise, subtract `dow` number days to get the first day
start_date = date - timedelta(dow)
# Now, add 6 for the last day of the week (i.e., count up to Saturday)
end_date = start_date + timedelta(6)
return (start_date, end_date)
Now you have start and end date of week you can find out week dates as you want. For more you can refer this

how to get first and last dates of past four months in python

I am trying to get the first and last dates of past four months. I have tried with following concept,
#staticmethod
def getDateRangeByMonth(year, month):
"""
:param year:
:param month:
:return:
"""
if year is None:
today = datetime.today()
year = today.year
if month is None:
today = datetime.today()
month = today.month
_, num_days = calendar.monthrange(year, month)
since = datetime.datetime.strptime(str(datetime.date(year, month,
1)), "%Y-%m-%d")
till = datetime.datetime.strptime(str(datetime.date(year, month,
num_days)), "%Y-%m-%d")
return since, till
for i in range(0,4):
today = datetime.today()
since, till = getDateRangeByMonth(today.year, today.month - i)
I know the concept will fall when the past month crosses previous year like if the month is jan or feb then the year have to change.
How to alter this or what am i going wrong with this
thanks in advance
In following code, the trick is that the datetime module offers timedelta to step one day sooner and date.replace to get first day of month. So here is a simple function:
def start_end_by_month(dat, delta):
d = dat.replace(day = 1)
till = []
since = []
for i in range(delta):
d = d - datetime.timedelta(days=1)
till.append(d)
d = d.replace(day=1)
since.append(d)
return since, till
Usage example:
>>> start_end_by_month(datetime.date(2008, 3, 10), 5)
([datetime.date(2008, 2, 1), datetime.date(2008, 1, 1), datetime.date(2007, 12, 1),
datetime.date(2007, 11, 1), datetime.date(2007, 10, 1)],
[datetime.date(2008, 2, 29), datetime.date(2008, 1, 31), datetime.date(2007, 12, 31),
datetime.date(2007, 11, 30), datetime.date(2007, 10, 31)])
showing that is easily skips a year boundary...
Here's a modified function, which gives you the desired number of previous months' start and end dates:
def getPrevDateRangeByMonth(year, month, prev):
if year is None:
today = datetime.date.today()
year = today.year
if month is None:
today = datetime.date.today()
month = today.month
since = []
till = []
for i in range(prev):
if month == 0:
year -= 1
month = 12
_, num_days = calendar.monthrange(year, month)
since.append(datetime.datetime.strptime(str(datetime.date(year, month, 1)), "%Y-%m-%d"))
till.append(datetime.datetime.strptime(str(datetime.date(year, month, num_days)), "%Y-%m-%d"))
month -= 1
return since, till
prev = 4
today = datetime.date.today()
since, till = getPrevDateRangeByMonth(2017, 3, prev)
for i in range(prev):
print(since[i], till[i])
I have changed the datetime.today() to datetime.date.today() as that's how it works for me, you should probably change it back.

Adding years in python

If I want to add 100 years in my program, why is it showing the wrong date?
import datetime
stringDate= "January 10, 1920"
dateObject= datetime.datetime.strptime(stringDate, "%B %d, %Y")
endDate= dateObject+datetime.timedelta(days=100*365)
print dateObject.date()
print endDate.date()
The number of seconds in a year is not fixed. Think you know how many days are in a year? Think again.
To perform period (calendar) arithmetic, you could use dateutil.relativedelta:
#!/usr/bin/env python
from datetime import date
from dateutil.relativedelta import relativedelta # $ pip install python-dateutil
print(date(1920, 1, 10) + relativedelta(years=+100))
# -> 2020-01-10
To understand, why d.replace(year=d.year + 100) fails, consider:
print(date(2000, 2, 29) + relativedelta(years=+100))
2100-02-28
Notice that 2100 is not a leap year while 2000 is a leap year.
If the only units you want to add is year then you could implement it using only stdlib:
from calendar import isleap
def add_years(d, years):
new_year = d.year + years
try:
return d.replace(year=new_year)
except ValueError:
if (d.month == 2 and d.day == 29 and # leap day
isleap(d.year) and not isleap(new_year)):
return d.replace(year=new_year, day=28)
raise
Example:
from datetime import date
print(add_years(date(1920, 1, 10), 100))
# -> 2020-01-10
print(add_years(date(2000, 2, 29), 100))
# -> 2100-02-28
print(add_years(date(2000, 2, 29), 4))
# -> 2004-02-29
You can't just add 100 * 365 days, because there are leap years with 366 days in that timespan. Over your 100 year span you are missing 25 days.
Better to just use the datetime.replace() method here:
endDate = dateObject.replace(year=dateObject.year + 100)
This can still fail for February 29th in a leap year, as depending on the number of years you add you'd end up with an invalid date. You could move back to February 28th in that case, or use March 31st; handle the exception thrown and switch to your chosen replacement:
years = 100
try:
endDate = dateObject.replace(year=dateObject.year + years)
except ValueError::
# Leap day in a leap year, move date to February 28th
endDate = dateObject.replace(year=dateObject.year + years, day=28)
Demo:
>>> import datetime
>>> dateObject = datetime.datetime(1920, 1, 10, 0, 0)
>>> dateObject.replace(year=dateObject.year + 100)
datetime.datetime(2020, 1, 10, 0, 0)
man 3 mktime
Anyone who ever did C knows the answer.
mktime automatically adds overflowing values to the next bigger unit. You just need to convert it back to a datetime.
For example you can feed it with 2019-07-40, which converts to 2019-08-09.
>>> datetime.fromtimestamp(mktime((2019, 7, 40, 0, 0, 0, 0, 0, 0)))
datetime.datetime(2019, 8, 9, 0, 0)
Or 2019-03-(-1) is converted to 2019-02-27:
>>> datetime.fromtimestamp(mktime((2019, 3, -1, 0, 0, 0, 0, 0, 0)))
datetime.datetime(2019, 2, 27, 0, 0)
So you just take your old date and add whatever you like:
now = datetime.datetime.now()
hundred_days_later = datetime.datetime.fromtimestamp(mktime((now.year, now.month, now.day + 100, now.hour, now.minute, now.second, 0, 0, 0)))
For the past 5 years, I've been using pandas Timestamp and Timedelta for everything date related.
For example, to add 3 years to any date in string format:
date = "2003-07-01"
date_start = Timestamp(date)
date_end = Timestamp(date_start.year+3, date_start.month, date_start.day)
date_end
# Out:
Timestamp('2006-07-01 00:00:00')
To add 40 days to a date, use Timedelta (it does not support years, or else we could have used it for the last problem):
date_end = date_start + Timedelta(40, unit="days")
date_end
# Out:
Timestamp('2003-08-10 00:00:00')

Python: Date manipulation code

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)

Best way to find the months between two dates

I have the need to be able to accurately find the months between two dates in python. I have a solution that works but its not very good (as in elegant) or fast.
dateRange = [datetime.strptime(dateRanges[0], "%Y-%m-%d"), datetime.strptime(dateRanges[1], "%Y-%m-%d")]
months = []
tmpTime = dateRange[0]
oneWeek = timedelta(weeks=1)
tmpTime = tmpTime.replace(day=1)
dateRange[0] = tmpTime
dateRange[1] = dateRange[1].replace(day=1)
lastMonth = tmpTime.month
months.append(tmpTime)
while tmpTime < dateRange[1]:
if lastMonth != 12:
while tmpTime.month <= lastMonth:
tmpTime += oneWeek
tmpTime = tmpTime.replace(day=1)
months.append(tmpTime)
lastMonth = tmpTime.month
else:
while tmpTime.month >= lastMonth:
tmpTime += oneWeek
tmpTime = tmpTime.replace(day=1)
months.append(tmpTime)
lastMonth = tmpTime.month
So just to explain, what I'm doing here is taking the two dates and converting them from iso format into python datetime objects. Then I loop through adding a week to the start datetime object and check if the numerical value of the month is greater (unless the month is December then it checks if the date is less), If the value is greater I append it to the list of months and keep looping through until I get to my end date.
It works perfectly it just doesn't seem like a good way of doing it...
Start by defining some test cases, then you will see that the function is very simple and needs no loops
from datetime import datetime
def diff_month(d1, d2):
return (d1.year - d2.year) * 12 + d1.month - d2.month
assert diff_month(datetime(2010,10,1), datetime(2010,9,1)) == 1
assert diff_month(datetime(2010,10,1), datetime(2009,10,1)) == 12
assert diff_month(datetime(2010,10,1), datetime(2009,11,1)) == 11
assert diff_month(datetime(2010,10,1), datetime(2009,8,1)) == 14
You should add some test cases to your question, as there are lots of potential corner cases to cover - there is more than one way to define the number of months between two dates.
One liner to find a list of datetimes, incremented by month, between two dates.
import datetime
from dateutil.rrule import rrule, MONTHLY
strt_dt = datetime.date(2001,1,1)
end_dt = datetime.date(2005,6,1)
dates = [dt for dt in rrule(MONTHLY, dtstart=strt_dt, until=end_dt)]
This worked for me -
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime('2011-08-15 12:00:00', '%Y-%m-%d %H:%M:%S')
date2 = datetime.strptime('2012-02-15', '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months + (12*r.years)
from dateutil import relativedelta
r = relativedelta.relativedelta(date1, date2)
months_difference = (r.years * 12) + r.months
You can easily calculate this using rrule from dateutil module:
from dateutil import rrule
from datetime import date
print(list(rrule.rrule(rrule.MONTHLY, dtstart=date(2013, 11, 1), until=date(2014, 2, 1))))
will give you:
[datetime.datetime(2013, 11, 1, 0, 0),
datetime.datetime(2013, 12, 1, 0, 0),
datetime.datetime(2014, 1, 1, 0, 0),
datetime.datetime(2014, 2, 1, 0, 0)]
Get the ending month (relative to the year and month of the start month ex: 2011 January = 13 if your start date starts on 2010 Oct) and then generate the datetimes beginning the start month and that end month like so:
dt1, dt2 = dateRange
start_month=dt1.month
end_months=(dt2.year-dt1.year)*12 + dt2.month+1
dates=[datetime.datetime(year=yr, month=mn, day=1) for (yr, mn) in (
((m - 1) / 12 + dt1.year, (m - 1) % 12 + 1) for m in range(start_month, end_months)
)]
if both dates are on the same year, it could also be simply written as:
dates=[datetime.datetime(year=dt1.year, month=mn, day=1) for mn in range(dt1.month, dt2.month + 1)]
My simple solution:
import datetime
def months(d1, d2):
return d1.month - d2.month + 12*(d1.year - d2.year)
d1 = datetime.datetime(2009, 9, 26)
d2 = datetime.datetime(2019, 9, 26)
print(months(d1, d2))
This post nails it! Use dateutil.relativedelta.
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime(str('2011-08-15 12:00:00'), '%Y-%m-%d %H:%M:%S')
date2 = datetime.strptime(str('2012-02-15'), '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months
Update 2018-04-20: it seems that OP #Joshkunz was asking for finding which months are between two dates, instead of "how many months" are between two dates. So I am not sure why #JohnLaRooy is upvoted for more than 100 times. #Joshkunz indicated in the comment under the original question he wanted the actual dates [or the months], instead of finding the total number of months.
So it appeared the question wanted, for between two dates 2018-04-11 to 2018-06-01
Apr 2018, May 2018, June 2018
And what if it is between 2014-04-11 to 2018-06-01? Then the answer would be
Apr 2014, May 2014, ..., Dec 2014, Jan 2015, ..., Jan 2018, ..., June 2018
So that's why I had the following pseudo code many years ago. It merely suggested using the two months as end points and loop through them, incrementing by one month at a time. #Joshkunz mentioned he wanted the "months" and he also mentioned he wanted the "dates", without knowing exactly, it was difficult to write the exact code, but the idea is to use one simple loop to loop through the end points, and incrementing one month at a time.
The answer 8 years ago in 2010:
If adding by a week, then it will approximately do work 4.35 times the work as needed. Why not just:
1. get start date in array of integer, set it to i: [2008, 3, 12],
and change it to [2008, 3, 1]
2. get end date in array: [2010, 10, 26]
3. add the date to your result by parsing i
increment the month in i
if month is >= 13, then set it to 1, and increment the year by 1
until either the year in i is > year in end_date,
or (year in i == year in end_date and month in i > month in end_date)
just pseduo code for now, haven't tested, but i think the idea along the same line will work.
Define a "month" as 1/12 year, then do this:
def month_diff(d1, d2):
"""Return the number of months between d1 and d2,
such that d2 + month_diff(d1, d2) == d1
"""
diff = (12 * d1.year + d1.month) - (12 * d2.year + d2.month)
return diff
You might try to define a month as "a period of either 29, 28, 30 or 31 days (depending on the year)". But you you do that, you have an additional problem to solve.
While it's usually clear that June 15th + 1 month should be July 15th, it's not usually not clear if January 30th + 1 month is in February or March. In the latter case, you may be compelled to compute the date as February 30th, then "correct" it to March 2nd. But when you do that, you'll find that March 2nd - 1 month is clearly February 2nd. Ergo, reductio ad absurdum (this operation is not well defined).
Here's how to do this with Pandas FWIW:
import pandas as pd
pd.date_range("1990/04/03", "2014/12/31", freq="MS")
DatetimeIndex(['1990-05-01', '1990-06-01', '1990-07-01', '1990-08-01',
'1990-09-01', '1990-10-01', '1990-11-01', '1990-12-01',
'1991-01-01', '1991-02-01',
...
'2014-03-01', '2014-04-01', '2014-05-01', '2014-06-01',
'2014-07-01', '2014-08-01', '2014-09-01', '2014-10-01',
'2014-11-01', '2014-12-01'],
dtype='datetime64[ns]', length=296, freq='MS')
Notice it starts with the month after the given start date.
Many people have already given you good answers to solve this but I have not read any using list comprehension so I give you what I used for a similar use case :
def compute_months(first_date, second_date):
year1, month1, year2, month2 = map(
int,
(first_date[:4], first_date[5:7], second_date[:4], second_date[5:7])
)
return [
'{:0>4}-{:0>2}'.format(year, month)
for year in range(year1, year2 + 1)
for month in range(month1 if year == year1 else 1, month2 + 1 if year == year2 else 13)
]
>>> first_date = "2016-05"
>>> second_date = "2017-11"
>>> compute_months(first_date, second_date)
['2016-05',
'2016-06',
'2016-07',
'2016-08',
'2016-09',
'2016-10',
'2016-11',
'2016-12',
'2017-01',
'2017-02',
'2017-03',
'2017-04',
'2017-05',
'2017-06',
'2017-07',
'2017-08',
'2017-09',
'2017-10',
'2017-11']
There is a simple solution based on 360 day years, where all months have 30 days.
It fits most use cases where, given two dates, you need to calculate the number of full months plus the remaining days.
from datetime import datetime, timedelta
def months_between(start_date, end_date):
#Add 1 day to end date to solve different last days of month
s1, e1 = start_date , end_date + timedelta(days=1)
#Convert to 360 days
s360 = (s1.year * 12 + s1.month) * 30 + s1.day
e360 = (e1.year * 12 + e1.month) * 30 + e1.day
#Count days between the two 360 dates and return tuple (months, days)
return divmod(e360 - s360, 30)
print "Counting full and half months"
print months_between( datetime(2012, 01, 1), datetime(2012, 03, 31)) #3m
print months_between( datetime(2012, 01, 1), datetime(2012, 03, 15)) #2m 15d
print months_between( datetime(2012, 01, 16), datetime(2012, 03, 31)) #2m 15d
print months_between( datetime(2012, 01, 16), datetime(2012, 03, 15)) #2m
print "Adding +1d and -1d to 31 day month"
print months_between( datetime(2011, 12, 01), datetime(2011, 12, 31)) #1m 0d
print months_between( datetime(2011, 12, 02), datetime(2011, 12, 31)) #-1d => 29d
print months_between( datetime(2011, 12, 01), datetime(2011, 12, 30)) #30d => 1m
print "Adding +1d and -1d to 29 day month"
print months_between( datetime(2012, 02, 01), datetime(2012, 02, 29)) #1m 0d
print months_between( datetime(2012, 02, 02), datetime(2012, 02, 29)) #-1d => 29d
print months_between( datetime(2012, 02, 01), datetime(2012, 02, 28)) #28d
print "Every month has 30 days - 26/M to 5/M+1 always counts 10 days"
print months_between( datetime(2011, 02, 26), datetime(2011, 03, 05))
print months_between( datetime(2012, 02, 26), datetime(2012, 03, 05))
print months_between( datetime(2012, 03, 26), datetime(2012, 04, 05))
Somewhat a little prettified solution by #Vin-G.
import datetime
def monthrange(start, finish):
months = (finish.year - start.year) * 12 + finish.month + 1
for i in xrange(start.month, months):
year = (i - 1) / 12 + start.year
month = (i - 1) % 12 + 1
yield datetime.date(year, month, 1)
You can also use the arrow library. This is a simple example:
from datetime import datetime
import arrow
start = datetime(2014, 1, 17)
end = datetime(2014, 6, 20)
for d in arrow.Arrow.range('month', start, end):
print d.month, d.format('MMMM')
This will print:
1 January
2 February
3 March
4 April
5 May
6 June
Hope this helps!
Get difference in number of days, months and years between two dates.
import datetime
from dateutil.relativedelta import relativedelta
iphead_proc_dt = datetime.datetime.now()
new_date = iphead_proc_dt + relativedelta(months=+25, days=+23)
# Get Number of Days difference bewtween two dates
print((new_date - iphead_proc_dt).days)
difference = relativedelta(new_date, iphead_proc_dt)
# Get Number of Months difference bewtween two dates
print(difference.months + 12 * difference.years)
# Get Number of Years difference bewtween two dates
print(difference.years)
Try something like this. It presently includes the month if both dates happen to be in the same month.
from datetime import datetime,timedelta
def months_between(start,end):
months = []
cursor = start
while cursor <= end:
if cursor.month not in months:
months.append(cursor.month)
cursor += timedelta(weeks=1)
return months
Output looks like:
>>> start = datetime.now() - timedelta(days=120)
>>> end = datetime.now()
>>> months_between(start,end)
[6, 7, 8, 9, 10]
You could use python-dateutil. See Python: Difference of 2 datetimes in months
just like range function, when month is 13, go to next year
def year_month_range(start_date, end_date):
'''
start_date: datetime.date(2015, 9, 1) or datetime.datetime
end_date: datetime.date(2016, 3, 1) or datetime.datetime
return: datetime.date list of 201509, 201510, 201511, 201512, 201601, 201602
'''
start, end = start_date.strftime('%Y%m'), end_date.strftime('%Y%m')
assert len(start) == 6 and len(end) == 6
start, end = int(start), int(end)
year_month_list = []
while start < end:
year, month = divmod(start, 100)
if month == 13:
start += 88 # 201513 + 88 = 201601
continue
year_month_list.append(datetime.date(year, month, 1))
start += 1
return year_month_list
example in python shell
>>> import datetime
>>> s = datetime.date(2015,9,1)
>>> e = datetime.date(2016, 3, 1)
>>> year_month_set_range(s, e)
[datetime.date(2015, 11, 1), datetime.date(2015, 9, 1), datetime.date(2016, 1, 1), datetime.date(2016, 2, 1),
datetime.date(2015, 12, 1), datetime.date(2015, 10, 1)]
It can be done using datetime.timedelta, where the number of days for skipping to next month can be obtained by calender.monthrange. monthrange returns weekday (0-6 ~ Mon-Sun) and number of days (28-31) for a given year and month.
For example: monthrange(2017, 1) returns (6,31).
Here is the script using this logic to iterate between two months.
from datetime import timedelta
import datetime as dt
from calendar import monthrange
def month_iterator(start_month, end_month):
start_month = dt.datetime.strptime(start_month,
'%Y-%m-%d').date().replace(day=1)
end_month = dt.datetime.strptime(end_month,
'%Y-%m-%d').date().replace(day=1)
while start_month <= end_month:
yield start_month
start_month = start_month + timedelta(days=monthrange(start_month.year,
start_month.month)[1])
`
it seems that the answers are unsatisfactory and I have since use my own code which is easier to understand
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime(str('2017-01-01'), '%Y-%m-%d')
date2 = datetime.strptime(str('2019-03-19'), '%Y-%m-%d')
difference = relativedelta.relativedelta(date2, date1)
months = difference.months
years = difference.years
# add in the number of months (12) for difference in years
months += 12 * difference.years
months
from datetime import datetime
from dateutil import relativedelta
def get_months(d1, d2):
date1 = datetime.strptime(str(d1), '%Y-%m-%d')
date2 = datetime.strptime(str(d2), '%Y-%m-%d')
print (date2, date1)
r = relativedelta.relativedelta(date2, date1)
months = r.months + 12 * r.years
if r.days > 0:
months += 1
print (months)
return months
assert get_months('2018-08-13','2019-06-19') == 11
assert get_months('2018-01-01','2019-06-19') == 18
assert get_months('2018-07-20','2019-06-19') == 11
assert get_months('2018-07-18','2019-06-19') == 12
assert get_months('2019-03-01','2019-06-19') == 4
assert get_months('2019-03-20','2019-06-19') == 3
assert get_months('2019-01-01','2019-06-19') == 6
assert get_months('2018-09-09','2019-06-19') == 10
#This definition gives an array of months between two dates.
import datetime
def MonthsBetweenDates(BeginDate, EndDate):
firstyearmonths = [mn for mn in range(BeginDate.month, 13)]<p>
lastyearmonths = [mn for mn in range(1, EndDate.month+1)]<p>
months = [mn for mn in range(1, 13)]<p>
numberofyearsbetween = EndDate.year - BeginDate.year - 1<p>
return firstyearmonths + months * numberofyearsbetween + lastyearmonths<p>
#example
BD = datetime.datetime.strptime("2000-35", '%Y-%j')
ED = datetime.datetime.strptime("2004-200", '%Y-%j')
MonthsBetweenDates(BD, ED)
Usually 90 days are NOT 3 months literally, just a reference.
So, finally, you need to check if days are bigger than 15 to add +1 to month counter. or better, add another elif with half month counter.
From this other stackoverflow answer i've finally ended with that:
#/usr/bin/env python
# -*- coding: utf8 -*-
import datetime
from datetime import timedelta
from dateutil.relativedelta import relativedelta
import calendar
start_date = datetime.date.today()
end_date = start_date + timedelta(days=111)
start_month = calendar.month_abbr[int(start_date.strftime("%m"))]
print str(start_date) + " to " + str(end_date)
months = relativedelta(end_date, start_date).months
days = relativedelta(end_date, start_date).days
print months, "months", days, "days"
if days > 16:
months += 1
print "around " + str(months) + " months", "(",
for i in range(0, months):
print calendar.month_abbr[int(start_date.strftime("%m"))],
start_date = start_date + relativedelta(months=1)
print ")"
Output:
2016-02-29 2016-06-14
3 months 16 days
around 4 months ( Feb Mar Apr May )
I've noticed that doesn't work if you add more than days left in current year, and that's is unexpected.
Here is my solution for this:
def calc_age_months(from_date, to_date):
from_date = time.strptime(from_date, "%Y-%m-%d")
to_date = time.strptime(to_date, "%Y-%m-%d")
age_in_months = (to_date.tm_year - from_date.tm_year)*12 + (to_date.tm_mon - from_date.tm_mon)
if to_date.tm_mday < from_date.tm_mday:
return age_in_months -1
else
return age_in_months
This will handle some edge cases as well where the difference in months between 31st Dec 2018 and 1st Jan 2019 will be zero (since the difference is only a day).
Assuming upperDate is always later than lowerDate and both are datetime.date objects:
if lowerDate.year == upperDate.year:
monthsInBetween = range( lowerDate.month + 1, upperDate.month )
elif upperDate.year > lowerDate.year:
monthsInBetween = range( lowerDate.month + 1, 12 )
for year in range( lowerDate.year + 1, upperDate.year ):
monthsInBetween.extend( range(1,13) )
monthsInBetween.extend( range( 1, upperDate.month ) )
I haven't tested this thoroughly, but it looks like it should do the trick.
Here is a method:
def months_between(start_dt, stop_dt):
month_list = []
total_months = 12*(stop_dt.year-start_dt.year)+(stop_dt.month-start_d.month)+1
if total_months > 0:
month_list=[ datetime.date(start_dt.year+int((start_dt+i-1)/12),
((start_dt-1+i)%12)+1,
1) for i in xrange(0,total_months) ]
return month_list
This is first computing the total number of months between the two dates, inclusive. Then it creates a list using the first date as the base and performs modula arithmetic to create the date objects.
I actually needed to do something pretty similar just now
Ended up writing a function which returns a list of tuples indicating the start and end of each month between two sets of dates so I could write some SQL queries off the back of it for monthly totals of sales etc.
I'm sure it can be improved by someone who knows what they're doing but hope it helps...
The returned value look as follows (generating for today - 365days until today as an example)
[ (datetime.date(2013, 5, 1), datetime.date(2013, 5, 31)),
(datetime.date(2013, 6, 1), datetime.date(2013, 6, 30)),
(datetime.date(2013, 7, 1), datetime.date(2013, 7, 31)),
(datetime.date(2013, 8, 1), datetime.date(2013, 8, 31)),
(datetime.date(2013, 9, 1), datetime.date(2013, 9, 30)),
(datetime.date(2013, 10, 1), datetime.date(2013, 10, 31)),
(datetime.date(2013, 11, 1), datetime.date(2013, 11, 30)),
(datetime.date(2013, 12, 1), datetime.date(2013, 12, 31)),
(datetime.date(2014, 1, 1), datetime.date(2014, 1, 31)),
(datetime.date(2014, 2, 1), datetime.date(2014, 2, 28)),
(datetime.date(2014, 3, 1), datetime.date(2014, 3, 31)),
(datetime.date(2014, 4, 1), datetime.date(2014, 4, 30)),
(datetime.date(2014, 5, 1), datetime.date(2014, 5, 31))]
Code as follows (has some debug stuff which can be removed):
#! /usr/env/python
import datetime
def gen_month_ranges(start_date=None, end_date=None, debug=False):
today = datetime.date.today()
if not start_date: start_date = datetime.datetime.strptime(
"{0}/01/01".format(today.year),"%Y/%m/%d").date() # start of this year
if not end_date: end_date = today
if debug: print("Start: {0} | End {1}".format(start_date, end_date))
# sense-check
if end_date < start_date:
print("Error. Start Date of {0} is greater than End Date of {1}?!".format(start_date, end_date))
return None
date_ranges = [] # list of tuples (month_start, month_end)
current_year = start_date.year
current_month = start_date.month
while current_year <= end_date.year:
next_month = current_month + 1
next_year = current_year
if next_month > 12:
next_month = 1
next_year = current_year + 1
month_start = datetime.datetime.strptime(
"{0}/{1}/01".format(current_year,
current_month),"%Y/%m/%d").date() # start of month
month_end = datetime.datetime.strptime(
"{0}/{1}/01".format(next_year,
next_month),"%Y/%m/%d").date() # start of next month
month_end = month_end+datetime.timedelta(days=-1) # start of next month less one day
range_tuple = (month_start, month_end)
if debug: print("Month runs from {0} --> {1}".format(
range_tuple[0], range_tuple[1]))
date_ranges.append(range_tuple)
if current_month == 12:
current_month = 1
current_year += 1
if debug: print("End of year encountered, resetting months")
else:
current_month += 1
if debug: print("Next iteration for {0}-{1}".format(
current_year, current_month))
if current_year == end_date.year and current_month > end_date.month:
if debug: print("Final month encountered. Terminating loop")
break
return date_ranges
if __name__ == '__main__':
print("Running in standalone mode. Debug set to True")
from pprint import pprint
pprint(gen_month_ranges(debug=True), indent=4)
pprint(gen_month_ranges(start_date=datetime.date.today()+datetime.timedelta(days=-365),
debug=True), indent=4)
Assuming that you wanted to know the "fraction" of the month that dates were in, which I did, then you need to do a bit more work.
from datetime import datetime, date
import calendar
def monthdiff(start_period, end_period, decimal_places = 2):
if start_period > end_period:
raise Exception('Start is after end')
if start_period.year == end_period.year and start_period.month == end_period.month:
days_in_month = calendar.monthrange(start_period.year, start_period.month)[1]
days_to_charge = end_period.day - start_period.day+1
diff = round(float(days_to_charge)/float(days_in_month), decimal_places)
return diff
months = 0
# we have a start date within one month and not at the start, and an end date that is not
# in the same month as the start date
if start_period.day > 1:
last_day_in_start_month = calendar.monthrange(start_period.year, start_period.month)[1]
days_to_charge = last_day_in_start_month - start_period.day +1
months = months + round(float(days_to_charge)/float(last_day_in_start_month), decimal_places)
start_period = datetime(start_period.year, start_period.month+1, 1)
last_day_in_last_month = calendar.monthrange(end_period.year, end_period.month)[1]
if end_period.day != last_day_in_last_month:
# we have lest days in the last month
months = months + round(float(end_period.day) / float(last_day_in_last_month), decimal_places)
last_day_in_previous_month = calendar.monthrange(end_period.year, end_period.month - 1)[1]
end_period = datetime(end_period.year, end_period.month - 1, last_day_in_previous_month)
#whatever happens, we now have a period of whole months to calculate the difference between
if start_period != end_period:
months = months + (end_period.year - start_period.year) * 12 + (end_period.month - start_period.month) + 1
# just counter for any final decimal place manipulation
diff = round(months, decimal_places)
return diff
assert monthdiff(datetime(2015,1,1), datetime(2015,1,31)) == 1
assert monthdiff(datetime(2015,1,1), datetime(2015,02,01)) == 1.04
assert monthdiff(datetime(2014,1,1), datetime(2014,12,31)) == 12
assert monthdiff(datetime(2014,7,1), datetime(2015,06,30)) == 12
assert monthdiff(datetime(2015,1,10), datetime(2015,01,20)) == 0.35
assert monthdiff(datetime(2015,1,10), datetime(2015,02,20)) == 0.71 + 0.71
assert monthdiff(datetime(2015,1,31), datetime(2015,02,01)) == round(1.0/31.0,2) + round(1.0/28.0,2)
assert monthdiff(datetime(2013,1,31), datetime(2015,02,01)) == 12*2 + round(1.0/31.0,2) + round(1.0/28.0,2)
provides an example that works out the number of months between two dates inclusively, including the fraction of each month that the date is in. This means that you can work out how many months is between 2015-01-20 and 2015-02-14, where the fraction of the date in the month of January is determined by the number of days in January; or equally taking into account that the number of days in February can change form year to year.
For my reference, this code is also on github - https://gist.github.com/andrewyager/6b9284a4f1cdb1779b10
Try this:
dateRange = [datetime.strptime(dateRanges[0], "%Y-%m-%d"),
datetime.strptime(dateRanges[1], "%Y-%m-%d")]
delta_time = max(dateRange) - min(dateRange)
#Need to use min(dateRange).month to account for different length month
#Note that timedelta returns a number of days
delta_datetime = (datetime(1, min(dateRange).month, 1) + delta_time -
timedelta(days=1)) #min y/m/d are 1
months = ((delta_datetime.year - 1) * 12 + delta_datetime.month -
min(dateRange).month)
print months
Shouldn't matter what order you input the dates, and it takes into account the difference in month lengths.

Categories