How can I subtract two dates in Python? - python

So basically what I want to do is to subtract the date of birth from todays date in order to get a persons age, I've successfully done this, but I can only get it to show the persons age in days.
dateofbirth = 19981128
dateofbirth = list(str(dateofbirth))
now = datetime.date.today()
yr = dateofbirth[:4]
yr = ''.join(map(str, yr))
month = dateofbirth[4:6]
month = ''.join(map(str, month))
day = dateofbirth[6:8]
day = ''.join(map(str, day))
birth = datetime.date(int(yr), int(month), int(day))
age = now - birth
print(age)
In this case, age comes out as days, is there any way to get it as xx years xx months and xx days?

You can use strptime:
>>> import datetime
>>> datetime.datetime.strptime('19981128', '%Y%m%d')
datetime.datetime(1998, 11, 28, 0, 0)
>>> datetime.datetime.now() - datetime.datetime.strptime('19981128', '%Y%m%d')
datetime.timedelta(5823, 81486, 986088)
>>> print (datetime.datetime.now() - datetime.datetime.strptime('19981128', '%Y%m%d'))
5823 days, 22:38:18.039365

The result of subtracting two dates in Python is a timedelta object, which just represents a duration. It doesn't "remember" when it starts, and so it can't tell you how many months have elapsed.
Consider that the period from 1st January to 1st March is "two months", and the period from 1st March to 28th April is "1 month and 28 days", but in a non-leap year they're both the same duration, 59 days. Actually, daylight savings, but let's not make this any more complicated than it needs to be to make the point ;-)
There may be a third-party library that helps you, but as far as standard Python libraries are concerned, AFAIK you'll have to roll your sleeves up and do it yourself by finding the differences of the day/month/year components of the two dates in turn. Of course, the month and day differences might be negative numbers so you'll have to deal with those cases. Recall how you were taught to do subtraction in school, and be very careful when carrying numbers from the month column to the days column, to use the correct number of days for the relevant month.

Related

I need to subtract a base date from the current date plus 7 days; but not sure how to account for change in year?

PaymentDate = datetime.now().timetuple().tm_yday + 7
NS_Date_Text = driver.find_element(By.ID, "custrecord_ps_inv_date_val").text
basedate = datetime.strptime(NS_Date_Text, '%m/%d/%Y')
basedate1 = datetime.timetuple(basedate).tm_yday
DaysUntilPayment = PaymentDate - basedate1
This code works so far for the year of 2019. But, I am not sure how to account for 2020 or 2018
So, I am converting the current date to the number of day in the year (January 1 would be 1 and December 31 would be 365/366) then adding 7 to that number. This is the Payment Date.
Then, I am finding a date on the webpage(BaseDate) and converting that number to the day of the year.
Then subtracting those two numbers.
I am not sure how well it's going to work if the current date is: January 10, 2020 (10th day) + 7 = day 17. But the base date is 12/28/2019 (362nd day).
The number I will get would be 345 and that's way too far ahead, while I need the number(DaysUntilPayment) to be 20.
I hope I was able to explain this well. Please lmk if you have any questions!
You are making this way more complicated than it needs to be. Python's datetime.date() objects know how to handle deltas themselves; if you subtract two date() objects you get a timedelta() instance, which has a .days attribute.
Next, you can create your own timedelta() object to add 7 days to 'today':
from datetime import date, timedelta
# 7 days from today
payment_date = date.today() + timedelta(days=7)
# find base date on the webpage
ns_date_text = driver.find_element(By.ID, "custrecord_ps_inv_date_val").text
basedate = datetime.strptime(ns_date_text, '%m/%d/%Y').date()
# calculate the difference in days between these two dates
# date - date = timedelta, so take the .days attribute from that result
days_until_payment = (payment_date - basedate).days
Note that I used only the date component of the datetime.strptime() result. You can do all this with datetime objects too, but then you may have to worry about timezones and such, and it's just easier not to have to do that.
These operations take care of details such as handling years, and more importantly, handling leap years:
>>> from datetime import date, datetime, timedelta
>>> payment_date = date(2020, 2, 22) + timedelta(days=7)
>>> payment_date # this is February 29th, a leap day!
datetime.date(2020, 2, 29)
>>> basedate = datetime.strptime("12/31/2019", '%m/%d/%Y').date # last day of 2019
>>> payment_date - basedate
datetime.timedelta(days=60)
>>> (payment_date - basedate).days # February 29th is 60 days later
60
For further details, see the datetime.date documentation, which has a Supported operations section, with:
date2 = date1 + timedelta
date2 is timedelta.days days removed from date1
timedelta = date1 - date2
This is exact, and cannot overflow. timedelta.seconds and timedelta.microseconds are 0, and date2 + timedelta == date1 after.

Why is my program outputting the wrong number of days?

So I am writing a program to work out the number of days you have been alive after imputting your birthday. There is a problem as i am getting the wrong number of days but can figure out why. i inputted my birthday as 04/04/19 and i got 730625 days which is clearly wrong.
import datetime #imports module
year = int(input("What year were you born in"))
month = int(input("What month where you born in (number)"))
date = int(input("What date is your birthday? "))
birthdate = datetime.date(date, month, year) #converts to dd/mm/yy
today = datetime.date.today() #todays date
daysAlive = (today - birthdate).days #calculates how many days since birth
print("You have been alive for {} days.".format(daysAlive)) #outputs result
I initially got the same error as you but then I checked my code and managed to fix my mistake.
So your DOB is 04/04/19, when you input that into datetime.date() and it looks at the value for year which is 19, it will treat that as 0019. As in 19 AD, not 2019. You should make sure that you input the full year.
Also like SimonN said, the parameters for datetime.date() are year, month, day, not the other way around.
You have the parameters the wrong way round in datetime.date they should be (year,month,day)
datetime takes arguments as (year, month, date). Note that you cannot enter year like 09 for 2009. Datetime will count it as 0009-MM-DD. You have to enter complete year in the input as 2009
...
birthdate = datetime.date(year, month, date)
...
So, with your input, the output for me is (It may differ with your timezone):
You have been alive for 170 days.
class datetime.date(year, month, day)
should be in the format yy/mm/dd.
Try this code for Python 3.6 or higher,
because of f-stings:
import datetime
year = int(input("What year were you born in: "))
month = int(input("What month were you born in (number): "))
day = int(input("What day were you born in: "))
birth_date = datetime.date(year, month, day) # converts to yy/mm/dd
today = datetime.date.today() # todays date
days_alive = (today - birth_date).days # calculates how many days since birth
print(f"You are {days_alive} days old.") # outputs result
Check the answer using other sources.

Get number of days in a specific month that are in a date range

Haven't been able to find an answer to this problem. Basically what I'm trying to do is this:
Take a daterange, for example October 10th to November 25th. What is the best algorithm for determining how many of the days in the daterange are in October and how many are in November.
Something like this:
def daysInMonthFromDaterange(daterange, month):
# do stuff
return days
I know that this is pretty easy to implement, I'm just wondering if there's a very good or efficient algorithm.
Thanks
Borrowing the algorithm from this answer How do I divide a date range into months in Python?
, this might work. The inputs are in date format, but can be changed to date strings if preferred:
import datetime
begin = '2018-10-10'
end = '2018-11-25'
dt_start = datetime.datetime.strptime(begin, '%Y-%m-%d')
dt_end = datetime.datetime.strptime(end, '%Y-%m-%d')
one_day = datetime.timedelta(1)
start_dates = [dt_start]
end_dates = []
today = dt_start
while today <= dt_end:
#print(today)
tomorrow = today + one_day
if tomorrow.month != today.month:
start_dates.append(tomorrow)
end_dates.append(today)
today = tomorrow
end_dates.append(dt_end)
out_fmt = '%d %B %Y'
for start, end in zip(start_dates,end_dates):
diff = (end - start).days
print('{} to {}: {} days'.format(start.strftime(out_fmt), end.strftime(out_fmt), diff))
result:
10 October 2018 to 31 October 2018: 21 days
01 November 2018 to 25 November 2018: 24 days
The problem as stated may not have a unique answer. For example what should you get from daysInMonthFromDaterange('Feb 15 - Mar 15', 'February')? That will depend on the year!
But if you substitute actual days, I would suggest converting from dates to integer days, using the first of the month to the first of the next month as your definition of a month. This is now reduced to intersecting intervals of integers, which is much easier.
The assumption that the first of the month always happened deals with months of different lengths, variable length months, and even correctly handles the traditional placement of the switch from the Julian calendar to the Gregorian. See cal 1752 for that. (It will not handle that switch for all locations though. Should you be dealing with a library that does Romanian dates in 1919, you could have a problem...)
You can use the datetime module:
from datetime import datetime
start = datetime(2018,10,10)
end = datetime(2018,11,25)
print((end - start).days)
Something like this would work:
def daysInMonthFromDaterange(date1, date2, month):
return [x for x in range(date1.toordinal(), date2.toordinal()) if datetime.date.fromordinal(x).year == month.year and datetime.date.fromordinal(x).month == month.month]
print(len(days_in_month(date(2018,10,10), date(2018,11,25), date(2018,10,01))))
This just loops through all the days between date1 and date2, and returns it as part of a list if it matches the year and month of the third argument.

Calculate Elapsed time in python in specific format

I am trying to write a program to determine the time and date corresponding to a elapsed number of seconds since 00: 00: 00 on 1 January 2016.
But i wanted my result in specific format.
i.e should be the corresponding hour (in military time), minute, second, day of the month, month name, year, and day of the week name (Sunday – Saturday).
for example,output should look like the following
23:59:32 2 January 2018 Tuesday
i tried to code this
import time
start = time.time()
#do something
end = time.time()
temp = end-start
print(temp)
hours = temp//3600
temp = temp - 3600*hours
minutes = temp//60
seconds = temp - 60*minutes
print('%d:%d:%d' %(hours,minutes,seconds))
But i could only get the hours, minutes and seconds part,
Is there any way i can get the month,year,and week name too ?
Also i'm trying to handle leap years too.since a leap year has 366 days with 29 days in February. Leap years are years that are evenly divisible by 4, with the exception of those evenly divisible by 100 but not 400.
To format a datetime you can use strftime().
import datetime
my_time = datetime.datetime(year=2018, month=1, day=2, hour=23, minute=59,second=32)
print (my_time.strftime("%X %-d %B %Y %A"))
If you want to change the format use this table for reference
You want to use the Datetime Module. Handling dates and times is trickier than it appears! That's why it's good that Python has datetime handling built-in. Datetime and timedelta objects account for leap years and leap seconds, and they handle operations like addition and subtraction 'intuitively'.
import datetime
def convert_seconds_since_jan_1(seconds):
JAN_1_2001 = datetime.datetime(year = 2001, month = 1, day = 1)
added_time = datetime.timedelta(seconds = seconds)
return (JAN_1_2001+added_time)
Getting the weekday is simply a matter of string formatting. Remember that weekdays are modulo 7!

Django/python date aggregation by month and quarter

I'm writing a feature that requires the average price of an item over different times (week, month, quarter etc.) Here's the model:
class ItemPrice(models.Model):
item = models.ForeignKey(Item)
date = models.DateField()
price = models.FloatField()
This model tracks the price of the item over time, with new Items being added at frequent, but not regular, intervals.
Finding the average price over the last week is easy enough:
ItemPrice.objects.filter(item__id = 1)
.filter(date_lt = TODAY)
.filter(date_gte = TODAY_MINUS_7_DAYS)
.filter(date_.aggregate(Avg('value'))
As a week always has 7 days, but what about month and quarter? They have different numbers of days...?
Thanks!
EDIT:
The app is for a finance org, 30-day months wont cut it I'm afraid, thanks for the suggestion!
The solution is two-part, first using the aggregation functions of django ORM, the second using python-dateutil.
from dateutil.relativedelta import relativedelta
A_MONTH = relativedelta(months=1)
month = ItemPrice.objects \
.filter(date__gte = date - A_MONTH) \
.filter(date__lt = date) \
.aggregate(month_average = Avg('price'))
month equals:
{'month_average': 40}
It's worth noticing that you can change the key of the month dictionary by changing the .aggregate() param.
dateutil's relativedelta can handle days, weeks, years and lots more. An excellent package, I'll be re-writing my home-grown hax.
import datetime
from dateutil import relativedelta, rrule
obj = self.get_object()
datenow = datetime.datetime.now()
quarters = rrule.rrule(
rrule.MONTHLY,
bymonth=(1, 4, 7, 10),
bysetpos=-1,
dtstart=datetime.datetime(datenow.year, 1, 1),
count=5)
first_day = quarters.before(datenow)
last_day = (quarters.after(datenow) - relativedelta.relativedelta(days=1))
quarter = Payment.objects.filter(
operation__department__cashbox__id=obj.pk,
created__range=(first_day, last_day)).aggregate(count=Sum('amount'))
inspiration from there
I would go for the 360-day calendar and not worry about these little inaccuracies. Just use the last 30 days for your "last month average" and the last 90 days for your "last quarter average".
First of all, are you interested in the past 7 days or the last week? If the answer is the last week, your query is not correct.
If it is past "n" days that concerns you, then your query is correct and I suppose you can just relax and use 30 days for a month and 90 days for a quarter.

Categories