This question already has answers here:
Python timedelta issue with negative values
(3 answers)
Closed 1 year ago.
I have a simple question today. I want to get the difference between the 2 dates and check if the difference is around ~5 minutes.
And I found a problem with getting the difference, to check.
I compared the same date with one that had a few minutes difference, and it printed a difference of -1 day? That doesn't make sense to me.
Test it yourself, if you want.
Compared Dates:
Date1: 2021-05-15 00:38:57.244000
Date2: 2021-05-15 02:40:42.245693
The printed difference was:
Diff: -1 day, 21:58:14.998307
So why is it at -1 day? Shouldn't the difference be just ~2 hours? And what's the best way to get the difference between the 2 dates? As I said, I want to check if the difference is smaller (or equal to) 5 minutes. How is that possible if the dates can be every time different?
Important info: The dates are always different because I check account creation dates.
I used this code to make the difference:
diff = date1 - date2
This problem can be solved by ordering the two date objects so that the diff comparison always returns a positive number. Then you can use a timedelta object to check if the diff is less than 5 minutes.
from datetime import datetime, timedelta
# Define dates as datetime objects
Date1 = datetime.fromisoformat("2021-05-15 00:38:57.244000")
Date2 = datetime.fromisoformat("2021-05-15 02:40:42.245693")
# Define 5 minutes as a timedelta object
five_minute_duration = timedelta(minutes=5)
Compare the two dates by subtracting the older date from the newer date. The result will be a timedelta object.
diff = Date2 - Date1
If you're not sure which date object is the older and younger, you could sort them. This will make the comparison stable and prevent the unexpected negative timedelta.
dates_sorted = sorted([Date2, Date1])
diff = dates_sorted[1] - dates_sorted[0]
Difference in hours. In this the example, the duration is 2.029... hours.
diff.total_seconds() / (60 * 60)
Check if the diff is smaller or equal to the 5 minute duration.
if diff <= five_minute_duration:
print("Yes")
else:
print("No")
You can do compare operation before minus operation. Example is as shown below.
from datetime import datetime
dt1 = datetime(2020, 4, 19, 12, 20)
dt2 = datetime(2020, 4, 21, 15, 2)
def get_date_diff(date1, date2):
diff = date2 - date1 if date1 < date2 else date1 - date2
return diff
def test():
print (get_date_diff(dt1, dt2))
test()
Create an upper and lower bound by creating two timedeltas with positive and negative values for the allowed difference
lower = timedelta(minutes=-5)
upper = timedelta(minutes=5)
Then check if the difference between the datetimes falls within those bounds
lower < date1 - date2 < upper
The advantage to this approach is that if this comparison is made many times lower and upper can be created just once and no sorting of the datetimes has to be performed
Related
I am wanting to take user input of date1 and date2 and calculate the difference to determine how many weeks are in between. My entire program is below. The example dates I'm using are:
date1 = 2023-03-15
date2 = 2022-11-09
Output is Number of weeks: 18 -- which is correct.
My 1st question that I need help in clarifying is why do I need the .days after the days = abs(date2-date1).days? I have searched for many hours via Google, stackoverflow, Youtube and Python docs https://docs.python.org/3.9/library/datetime.html?highlight=datetime#module-datetime. I'm pretty new to Python and reading the docs sometimes trips me up, so please forgive if it's in there -- I've struggled reading through some of it. Why is the .days needed? I know that if I remove .days, the output is: Number of weeks: 18 days, 0:00:00. Where is the documentation on needing the .days listed in the datetime module docs??? Can someone help me understand this please?
My 2nd question is why do I get Number of weeks: 0 when I change .days to .seconds? (this is when I was testing things and comment out the weeks = days//7 and print out days) The one part in the docs that I think addresses this the following: https://docs.python.org/3.9/library/datetime.html?highlight=datetime#module-datetime:~:text=the%20given%20year.-,Supported%20operations%3A,!%3D.%20The%20latter%20cases%20return%20False%20or%20True%2C%20respectively.,-In%20Boolean%20contexts.... and if this is correct, am I reading it correctly that if the difference in dates are to be determined, only "days" are returned, and thus no seconds or microseconds?
Thank you for your help! Code below:
#Find the number of weeks between two given dates
from datetime import datetime
#User input for 1st date in YYYY-MM-DD format
date1 = input("Enter 1st date in YYYY-MM-DD format: ")
date1 = datetime.strptime(date1, "%Y-%m-%d")
#User input for 2nd date in YYYY-MM-DD format
date2 = input("Enter 2nd date in YYYY-MM-DD format: ")
date2 = datetime.strptime(date2, "%Y-%m-%d")
#Calculate the weeks between the 2 given dates
days = abs(date2-date1).days
weeks = days//7
print("Number of weeks: ", weeks)
Output-correct answer with .days included:
Enter 1st date in YYYY-MM-DD format: 2023-03-15
Enter 2nd date in YYYY-MM-DD format: 2022-11-09
Number of weeks: 18
Output-with no .days added:
Enter 1st date in YYYY-MM-DD format: 2023-03-15
Enter 2nd date in YYYY-MM-DD format: 2022-11-09
Number of weeks: 18 days, 0:00:00
Output-(regarding 2nd question with the .seconds put in place of .days:
Enter 1st date in YYYY-MM-DD format: 2023-03-15
Enter 2nd date in YYYY-MM-DD format: 2022-11-09
Number of weeks: 0
Subtracting two datetime.datetime objects returns a datetime.timedelta object:
>>> date1 = datetime.strptime('2023-03-15', "%Y-%m-%d")
>>> date2 = datetime.strptime('2022-11-09', "%Y-%m-%d")
>>> date2-date1
datetime.timedelta(days=-126)
>>>
From the docs:
Only days, seconds and microseconds are stored internally. Arguments are converted to those units:
A millisecond is converted to 1000 microseconds.
A minute is converted to 60 seconds.
An hour is converted to 3600 seconds.
A week is converted to 7 days.
Here is some example usage of datetime.timedelta objects. So for your second question, I believe that you're right; for the difference between two days, there are no .seconds, as it is strictly a difference of days. For .seconds to be nonzero, you'd have to have some component of the difference that is larger than a 1,000,000 microseconds but smaller than 86,400 seconds, I suppose.
TL;DR: The answer to both of your questions is "because that is a property of the datetime.timedelta class."
More fully, the date1 and date2 objects you create in your code are both instances of the datetime.datetime class. The - operation between them makes a timedelta object.
Why is the .days needed to avoid printing ", 0:00:00"?
By default all the date information in the timedelta object you created with the operation abs(date2-date1) is printed (including seconds and microseconds, even after modifying it with the //7 operation). When you use the . operator, you access the days attribute of the timedelta object, and only that attribute's value is used.
Why do I get "Number of weeks: 0" when I change .days to .seconds?
The value of the seconds attribute of the timedelta object you created with the operation abs(date2-date1) is integer 0.
See below:
>>> from datetime import datetime
>>> date1 = "2023-03-15"
>>> date1, type(date1)
('2023-03-15', <class 'str'>)
>>> date1 = datetime.strptime(date1, "%Y-%m-%d")
>>> date1, type(date1)
(datetime.datetime(2023, 3, 15, 0, 0), <class 'datetime.datetime'>)
>>> date2 = "2022-11-09"
>>> date2, type(date2)
('2022-11-09', <class 'str'>)
>>> date2 = datetime.strptime(date2, "%Y-%m-%d")
>>> date2, type(date2)
(datetime.datetime(2022, 11, 9, 0, 0), <class 'datetime.datetime'>)
>>> abs(date2-date1), type(abs(date2-date1))
(datetime.timedelta(days=126), <class 'datetime.timedelta'>)
>>> abs(date2-date1).days, type(abs(date2-date1).days)
(126, <class 'int'>)
>>> abs(date2-date1).seconds, type(abs(date2-date1).seconds)
(0, <class 'int'>)
See also: this discussion.
To be precise you calculate difference in 7 days intervals, but the week is actualy a time period which starts on Monday (or Sunday) and ends on Sunday (or Monday). So the difference between 2022-10-01 (Sat) and 2022-10-04 (Tue) in weeks of year is 1, but in days is 3 (0 7days intervals in your case).
So if you need to find the distance between two dates in weeks of year you have to take account of the weekdays:
from datetime import date
d1 = date(2022,10,18)
d2 = date(2022,10,5)
w1 = d1.weekday()
w2 = d2.weekday()
# dfference in weeks of year = 2
((d1-d2).days - (w1-w2))/7 # 2.0
# difference in days = 13
d1-d2 # datetime.timedelta(days=13)
I need to calculate the difference between 2 differently formatted ISO dates. For example, 2019-06-28T05:28:14Z and 2019-06-28T05:28:14-04:00. Most of the answers here focus on only one format or another, i.e Z-formatted.
Here is what I have attempted using this library iso8601:
import iso8601
date1 = iso8601.parse_date("2019-06-28T05:28:14-04:00")
date2 = iso8601.parse_date("2019-06-28T05:28:14Z")
difference = date2 - date1
>>> datetime.timedelta(days=-1, seconds=75600)
I have also tried to replace Z with -00:00 but the difference is the same:
date2 = iso8601.parse_date("2019-06-28T05:28:14Z".replace("Z", "-00:00")
If I understand this correctly, it should show a difference of 4 hours. How do I calculate the difference in hours/days between 2 different date formats?
I am using Python 3.8.1.
I have used Pandas module but I think is the same with iso8601.
To have the right difference I had to specify the same timezone in parsing function, as it follows:
import pandas as pd
date1 = pd.to_datetime("2019-06-28T05:28:14-04:00",utc=True)
date2 = pd.to_datetime("2019-06-28T05:28:14Z",utc=True)
Then my difference is expressed in a Timedelta format:
difference = (date2 - date1)
print(difference)
>> Timedelta('-1 days +20:00:00')
A timedelta of -1 days and 20h means 4 hours, infact if I convert the total seconds in hours I obtain:
print(difference.total_seconds()//3600)
>> -4
I hope this could be of help.
An alternative is using the metomi-isodatetime package. It's created by the Met Office, so it must be standards-compliant.
Also, the package has no other dependencies, so it's 'lightweight'.
from metomi.isodatetime.parsers import TimePointParser
date1_s = "2019-06-28T05:28:14Z"
date2_s = "2019-06-28T05:28:14-04:00"
date1 = TimePointParser().parse(date1_s)
print(date1)
date2 = TimePointParser().parse(date2_s)
print(date2)
datediff = date2 - date1
print(type(datediff))
print(datediff)
print(datediff.hours)
#
Running the above will produce the following output:
2019-06-28T05:28:14Z
2019-06-28T05:28:14-04:00
<class 'metomi.isodatetime.data.Duration'>
PT4H
4
from datetime import datetime as dt
I have 2 datetime fields
dt.now() returns 2019-01-08 11:46:26.035303
This is PST
x is my dataset
x['CreatedDate'] returns 2019-01-08T20:35:47.000+0000
dt.strptime(x['CreatedDate'.split('.')[0],'%Y-%m-%dT%H:%M:%S)) - datetime.timedelta(hours=8) returns 2019-01-08 08:43:33
I subtract the two,
tdelta = dt.now() - (dt.strptime(x['CreatedDate'.split('.')[0],'%Y-%m-%dT%H:%M:%S)) - datetime.timedelta(hours=8))
which is 2019-01-08 11:46:26.035303 - 2019-01-08 08:43:33
The difference should be ~3 hours but the result I'm getting is -1 day, 11:02:53.039790
-13H 12M 53S
I'm confused as to what is being returned.
Disclaimer
I am having a tough time making the datetime objects that you made. So, my answer will not be a direct solution to your exact problem.
I dont have x defined in my code. If you supply it, I can adjust my answer to be more specific.
Answer
But if you use this code:
import datetime as dt
first_time = dt.datetime(2019, 1, 8, 8, 43, 33) #This is a good way to make a datetime object
To make your datetime object then this code below will make the correct calculations and print it effectively for you:
second_time = dt.datetime.now()
my_delta = first_time - second_time
print("Minutes: " + str(my_delta.total_seconds()/60))
print("Hours: " + str(my_delta.total_seconds()/3600))
print("Days: " + str(my_delta.total_seconds()/3600/24))
Note
dt.datetime takes (year, month, day, hour, minute, second) here but dt.datetime.now() is making one with microseconds as well (year, month, day, hour, minute, second, microseconds). The function can handle being given different time specificities without error.
Note 2
If you do print(my_delta) and get something like: -1 day, 16:56:54.481901 this will equate to your difference if your difference is Hours: -7.051532805277778 This is because 24-16.95 = -7.05
The issue is with the subtraction of datetime.timedelta(hours=8) I removed that from changed the dt.now to dt.utcnow() and it works fine.
I have two date time objects
`statrt_time` and `end_time`
my code is
if self.book_from and self.book_to:
fmt = '%Y-%m-%d %H:%M:%S'
s = datetime.strptime(self.book_from,fmt) #start date
e = datetime.strptime(self.book_to,fmt) #end date
diff = e - s
total_seconds=diff.seconds
time_diff = (total_seconds/3600.0)
no_of_units = (time_diff/4)
if(e<s):
self.units = 0
else:
self.units = math.ceil(no_of_units)
Here when I subtract time within the same day it is giving the correct difference. But when the day is changed, it is not calculating the day difference but only giving time difference. How can I add day difference also?
Use total_seconds() instead of seconds.
timedelta.seconds just shows "second" part of the difference, while total_seconds() shows the duration of the difference in seconds. See Mureinik's answer for more details.
So, use this:
total_seconds=diff.total_seconds()
total_seconds is a timedelta object which stores the difference between two datetimes using three fields - days, seconds and miliseconds. Your snippet just uses the seconds attributes instead of the entire difference. The total_seconds() method takes care of this for you and returns, well, the total number of seconds between two datatimes.
I got another way of doing.. BUT A WORK AROUND..
if self.book_from and self.book_to:
fmt = '%Y-%m-%d %H:%M:%S'
s = datetime.strptime(self.book_from,fmt) #start date
e = datetime.strptime(self.book_to,fmt) #end date
diff = e - s
days=diff.days// convert difference to day instead of seconds//
days_seconds=0
if(days>0): //Checking whether the difference exceeds a day//
days_seconds=days*24*3600 //If so convert it to seconds//
total_seconds=diff.seconds+days_seconds
time_diff = (total_seconds/3600.0)
please consider this problem:
I have a date in the past from where I start adding periods. When adding these periods results in a date greater than today, I want to stop, and check what the last date is.
This is a functionality for calculating debit dates in a membership. A member joins, say, 2007-01-31. He is debited every month. Let's say today is 2013-03-29 (it actually is atm). So I need to start counting months since 2007-01-31 and when I get past today's date, I need to stop. I can then see that the next debit date is 2013-03-31.
I am using the dateutil library to implement this, adding relativedelta's in a while loop until I surpass the current date. (I know it's probably not the best way, but I'm still quite new at Python and this is a proof-of-concept). The problem is that when I add a month to 2007-01-31, the next date is 2007-02-28, which is correct. But the next iteration the date is 2007-03-28, because dateutil doesn't recognize the 28th as the last day of the month to keep it intact and iterate to the last day of march. Ofcourse, that's a perfectly valid implementation. I then experimented with dateutils rrule object, but it has the same principles. It outputs a list of dates, but it simply skips the months that don't have enough days.
period = rrule(MONTHLY, interval=1, dtstart=datetime.date(2012, 5, 31), until=datetime.date(2013, 3, 29))
print(list(period))
Then I thought of a different approach:
If I could count the number of periods in the timespan between 2007-01-31 and 2013-03-29, I can add those number of periods to the startdate, and dateutil would return the right date.
The problem there is that the period isn't always one month. It can also be four weeks, a quarter or a year, for example.
I couldn't find a way to take a relativedelta and divide it with another relativedelta, to get a number of times the latter goes in the first.
If anyone can point me in the right direction I would appreciate it. Is there, for example, a library that can do this (divide timespans by each other, and output the result in a given timeblock, like months or weeks)? Is there perhaps a datediff function that accepts a period as an input (I know for example in vbscript you can get the difference between two dates in whatever period you want, be it weeks, months, days, whatever). Is there perhaps a totally different solution?
For completeness, I will include the code, but I think the text explains it all already:
def calculate(self, testdate=datetime.date.today()):
self._testdate = testdate
while self.next < self._testdate:
self.next += self._ddinterval
self.previous = self.next - self._ddinterval
return self.next
Thanks,
Erik
edit: I now have a solution that does what it's supposed to, but it's hardly Pythonic, elegant or speedy. So the question remains the same, if anyone can come up with a better solution, please do. Here's what I came up with:
def calculate(self, testdate=datetime.date.today()):
self._testdate = testdate
start = self.next
count = 0
while self.next < self._testdate:
count += 1
self.next = start + (count * self._ddinterval)
self.previous = self.next - self._ddinterval
return self.next
Instead of using the new value after adding your period as the starting point for the next loop, create an ever-increasing delta instead:
today = datetime.date.today()
start = datetime.date(2007, 1, 31)
period = relativedelta(months=1)
delta = period
while start + delta < today:
delta += period
next = start + delta
This results in:
>>> import datetime
>>> from dateutil.relativedelta import relativedelta
>>> today = datetime.date.today()
>>> start = datetime.date(2007, 1, 31)
>>> period = relativedelta(months=1)
>>> delta = period
>>> while start + delta < today:
... delta += period
...
>>> delta
relativedelta(years=+6, months=+3)
>>> start + delta
datetime.date(2013, 4, 30)
This works for any variable period lengths including quarters and years. For periods measured in exact weeks or days you can use a timedelta() object too, but this is a generic solution.
You cannot 'divide' time periods when using variable-width periods such as months and quarters. For time periods measured in whole days you can simply convert the difference between two dates to days (from the timedelta) and then get the modulus:
period = datetime.timedelta(days=28) # 4 weeks
delta = today - start
remainder = delta.days % period.days
end = today + datetime.timedelta(days=remainder)
which gives:
>>> period = datetime.timedelta(days=28) # 4 weeks
>>> delta = today - start
>>> remainder = delta.days % period.days
>>> today + datetime.timedelta(days=remainder)
datetime.date(2013, 4, 17)
If your delta is variable with respect to base time (i.e., 1 month can mean any of 28-31 days depending), then you're stuck with a loop.
If delta is a constant day count, however, you can sidestep iteration by converting to integers and doing a modulo operation.