Why aren't identical datetimes equal? - python

I'm working on a simple Python3 script that considers data in five-minute increments. Thanks to this post, I have code which takes any Python datetime object and then rounds it down to the nearest five minutes. (:00, :05, :10, :15, etc.) Note that I cannot use pandas.
Now I need to be able to compare that "rounded-down" datetime with other datetimes, and here I'm running into a problem. Consider this test code:
import sys
from datetime import datetime
from datetime import timedelta
def roundDownDateTime(dt):
# Arguments:
# dt datetime object
delta = timedelta(minutes=1) * (dt.minute % 5)
return dt - delta
def testAlarm(testDate):
# Arguments:
# testDate datetime object
currDate = roundDownDateTime( datetime.now() ) # currDate is a DateTime object, rounded down to 5 mins
print("currDate: "+currDate.strftime("%Y%m%d%H%M"))
print("testDate: "+testDate.strftime("%Y%m%d%H%M"))
if(currDate == testDate):
print("ALARM!!!!")
def main():
testDate = datetime.strptime(sys.argv[1], "%Y%m%d%H%M")
testAlarm(testDate)
if __name__ == "__main__":
main()
The code does all of the following:
The main() function takes a string you enter on the command line,
then converts it into a "%Y%m%d%H%M" datetime
Your datetime is rounded down to the last five minute increment
In testAlarm(), your date is compared with the current date, also in
"%Y%m%d%H%M" format, also rounded down five minutes.
If the current date matches the cmd line argument, you should get an
"ALARM!!! in the output.
Here's the actual output, run on my Ubuntu machine:
me#unbuntu1$ date
Tue Jan 17 14:27:41 UTC 2023
me#unbuntu1$
me#unbuntu1$ python3 toy04.py 202301171425
currDate: 202301171425
testDate: 202301171425
me#unbuntu1$
Okay: Although I'm rounding down my date to match the "rounded-down" version of the current date, the if(currDate == testDate): line of code is still evaluating to False. While both datetimes appear equal in the "%Y%m%d%H%M" format, they are somehow not equal.
My first thought was that maybe the "rounded down" datetime still retained some residual seconds or microseconds even after the rounding part? So I modified my function to this:
def roundDownDateTime(dt):
# Arguments:
# dt DateTime object
delta = timedelta(minutes=1) * (dt.minute % 5)
dt = dt - delta
dt.replace(second=0, microsecond=0)
return dt
But that makes no difference; I still get the exact same output as before.
Normally, you would only care if currDate > testDate for alarming purposes. But in my case, I must be able to compare datetimes for equality after one (or more) of them has been through the roundDownDateTime() function. What am I missing? Is my roundDownDateTime() function faulty? Thank you.

dt.replace returns a new datetime object; it does not modify dt in place.
def roundDownDateTime(dt):
# Arguments:
# dt DateTime object
delta = timedelta(minutes=1) * (dt.minute % 5)
dt = dt - delta
return dt.replace(second=0, microsecond=0)

Related

Python: ValueError raised when parsing a datetime string

The following ValueError is being raised while running the following code. The date is passed as a string from an another component, where I need to strip out the time.
ValueError: time data '2022-03-24T14:02:24.6413975' does not match format '%Y-%m-%d %H:%M:%S,%f'
The code:
from datetime import datetime
date='2022-03-24T14:02:24.6413975'
time = datetime.strptime(date, "%Y-%m-%d %H:%M:%S,%f")
if time > '09:30' :
print("do some thing")
The primary issue you're facing is the datetime format, as explained by the error message.
The .%f formatter can only accept six decimal places.
The T is missing from the format string.
There is a comma before the %f formatter, where there should be a full stop.
Therefore, this is the formatting string you need:
'%Y-%m-%dT%H:%M:%S.%f'
Additionally, time can be parsed from a datetime object simply by calling the .time() function as follows. String parsing should not be used, if at all possible.
time = dt.datetime.strptime(date, "%Y-%m-%dT%H:%M:%S.%f").time()
Next, the if statement should compare datetime object to datetime object, as:
if time > dt.time(9,30):
...
Therefore, the complete solution is as follows:
import datetime as dt
# Fractional seconds reduced to 6 decimal places.
date = '2022-03-24T14:02:24.641397'
# Use the .time() function to extract the time.
time = dt.datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%f').time()
# Compare datetime object to datetime object.
if time > dt.time(9,30):
print('Do some stuff ...')
This should work:
import datetime
date='2022-03-24T14:02:24.6413975'
time = date.split('T')[1].split(':')
time = datetime.time(int(time[0]), int(time[1]), int(time[2].split('.')[0]), int(time[2].split('.')[1][:6]))
if time > datetime.time(9, 30) :
print("do some thing")
Output:
do some thing
This just takes date, splits it T, splits the second part of the resulting string at every :, and passes all of them to datetime.time. The last two arguments to datetime.time have to be split a the decimal to get the microseconds, and the last one has to be shortened because of the limit on how long datetime allows microseconds to be.
Hope this may help:
def stripTime():
date='2022-03-24T14:02:24.6413975'
date=date.split('T')
print(date[1])
stripTime()
Output:
14:02:24.6413975

How to define a difference in time in seconds between a timestamp saved as a string and a current UTC timestamp

I am testing the difference between an actual UTC time and timestamp when my object was saved in a table (UTC). It must be not more than 60 seconds.
Example of timestamp_from_table (string from my site): 2021-02-05 13:51:52
After researching for options to make this, I came to this approach:
timestamp_from_table = driver.find_element_by_css_selector("my_locator").text
current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime()) # current time converted to string
current_time_truncated = datetime.strptime(current_time, "%Y-%m-%d %H:%M:%S") # cutting milliseconds
date_time_obj = datetime.strptime(timestamp_from_table, '%Y-%m-%d %H:%M:%S') # converting string timestamp to a datetime object
time_difference = current_time_truncated - date_time_obj
result = time_difference.seconds # datetime.timedelta represented in seconds
assert result in range(1, 60), error()
It works just fine, but probably there is a shorter way to compare a difference between a timestamp saved as string and actual utc timestamp. Thanks for any advice.
I'm reading between the lines a bit, but it sounds like at a high level your goal is to calculate the elapsed seconds between two times. If I'm right about that, here is a typical way to do it in Python:
import datetime
import time
previous = datetime.datetime.now()
time.sleep(5) # Added to simulate the passing of time for demonstration purposes
current = datetime.datetime.now()
elapsed_seconds = (current - previous) / datetime.timedelta(seconds=1)
"Division" by timedelta is the key to getting elapsed seconds (or any other time unit) between two datetime objects. While not UTC specific hopefully this shines a light on an approach that works for you.

How to add a certain time to a datetime?

I want to add hours to a datetime and use:
date = date_object + datetime.timedelta(hours=6)
Now I want to add a time:
time='-7:00' (string) plus 4 hours.
I tried hours=time+4 but this doesn't work. I think I have to int the string like int(time) but this doesn't work either.
Better you parse your time like below and access datetime attributes for getting time components from the parsed datetime object
input_time = datetime.strptime(yourtimestring,'yourtimeformat')
input_seconds = input_time.second # for seconds
input_minutes = input_time.minute # for minutes
input_hours = input_time.hour # for hours
# Usage: input_time = datetime.strptime("07:00","%M:%S")
Rest you have datetime.timedelta method to compose the duration.
new_time = initial_datetime + datetime.timedelta(hours=input_hours,minutes=input_minutes,seconds=input_seconds)
See docs strptime
and datetime format
You need to convert to a datetime object in order to add timedelta to your current time, then return it back to just the time portion.
Using date.today() just uses the arbitrary current date and sets the time to the time you supply. This allows you to add over days and reset the clock to 00:00.
dt.time() prints out the result you were looking for.
from datetime import date, datetime, time, timedelta
dt = datetime.combine(date.today(), time(7, 00)) + timedelta(hours=4)
print dt.time()
Edit:
To get from a string time='7:00' to what you could split on the colon and then reference each.
this_time = this_time.split(':') # make it a list split at :
this_hour = this_time[0]
this_min = this_time[1]
Edit 2:
To put it all back together then:
from datetime import date, datetime, time, timedelta
this_time = '7:00'
this_time = this_time.split(':') # make it a list split at :
this_hour = int(this_time[0])
this_min = int(this_time[1])
dt = datetime.combine(date.today(), time(this_hour, this_min)) + timedelta(hours=4)
print dt.time()
If you already have a full date to use, as mentioned in the comments, you should convert it to a datetime using strptime. I think another answer walks through how to use it so I'm not going to put an example.

string convert to datetime timezone

I want to linear interpolation some points between two time string.
So I try to convert string to datetime then insert some point then convert datetime to string. but it seems the timezone not correct.
In below example. I wish to insert one point between 9-28 11:07:57.435" and "9-28 12:00:00.773".
#!/usr/bin/env python
import numpy as np
from time import mktime
from datetime import datetime
#-----------------------------------------#
def main():
dtstr = [
"9-28 11:07:57.435",
"9-28 12:00:00.773"
]
print "input",dtstr
dtlst = str2dt(dtstr)
floatlst = dt2float(dtlst)
bins = 3
x1 = list(np.arange(floatlst[0],floatlst[-1],(floatlst[-1]-floatlst[0])/bins))
dtlst = float2dt(x1)
dtstr = dt2str(dtlst)
print "output",dtstr
return
def str2dt(strlst):
dtlst = [datetime.strptime("2014-"+i, "%Y-%m-%d %H:%M:%S.%f") for i in strlst]
return dtlst
def dt2float(dtlst):
floatlst = [mktime(dt.timetuple()) for dt in dtlst]
return floatlst
def dt2str(dtlst):
dtstr = [dt.strftime("%Y-%m-%d %H:%M:%S %Z%z") for dt in dtlst]
return dtstr
def float2dt(floatlst):
dtlst = [datetime.utcfromtimestamp(seconds) for seconds in floatlst]
return dtlst
#-----------------------------------------#
if __name__ == "__main__":
main()
The output looks like:
input ['9-28 11:07:57.435', '9-28 12:00:00.773']
output ['2014-09-28 16:07:57 ', '2014-09-28 16:25:18 ', '2014-09-28 16:42:39 ']
Two questions here:
The input and output has 4 hours differ (9-28 16:07:57 to 9-28 11:07:57). I guess it caused by timezone but not sure how to fix it.
I wish the first and last point the same as input, but now it seems the last point is less than the input last point (16:42:39 vs 12:00:00).
Q1. You're right about the timezones, you're using time.mktime which converts struct_time to seconds assuming the input is local time, but then using datetime.utcfromtimestamp which (naturally) converts to utc. Use datetime.fromtimestamp instead to keep everything in local time.
Q2. As with the native Python range/xrange, when you do numpy.arange(x, y, z), the result starts with x and goes upto, but not including y (except in weird floating point roundoff cases. Don't rely on this behaviour). if you want consistent behaviour on the end points w/ floating values, use numpy.linspace
On the other hand, why convert datetime to seconds, then go back again? datetime objects support addition and subtraction. Below would be my suggestion.
from time import mktime, localtime
from datetime import datetime
from copy import copy
def main():
input_timestrings = ["9-28 11:07:57.435", "9-28 12:00:00.773"]
input_datetimes = timestrings_to_datestimes(input_timestrings)
start_datetime = input_datetimes[0]
end_datetime = input_datetimes[1]
# subtraction between datetime objects returns a timedelta object
period_length = end_datetime - start_datetime
bins = 3
# operation w/ timedelta objects and datetime objects work pretty much as you'd expect it to
delta = period_length / bins
datetimes = list(custom_range(start_datetime, end_datetime + delta, delta))
output_timestrings = datetimes_to_timestrings(datetimes)
print output_timestrings
return
def timestrings_to_datetimes(timestrings):
datetimes = [datetime.strptime("2014-"+timestring, "%Y-%m-%d %H:%M:%S.%f") for timestring in timestrings]
return datetimes
def datetimes_to_timestrings(datetimes):
timestrings = [datetime_.strftime("%Y-%m-%d %H:%M:%S %Z%z") for datetime_ in datetimes]
return timestrings
def custom_range(start, end, jump):
x = start
while x < end:
yield x
x = x + jump
if __name__ == "__main__":
main()

How to calculate the time interval between two time strings

I have two times, a start and a stop time, in the format of 10:33:26 (HH:MM:SS). I need the difference between the two times. I've been looking through documentation for Python and searching online and I would imagine it would have something to do with the datetime and/or time modules. I can't get it to work properly and keep finding only how to do this when a date is involved.
Ultimately, I need to calculate the averages of multiple time durations. I got the time differences to work and I'm storing them in a list. I now need to calculate the average. I'm using regular expressions to parse out the original times and then doing the differences.
For the averaging, should I convert to seconds and then average?
Yes, definitely datetime is what you need here. Specifically, the datetime.strptime() method, which parses a string into a datetime object.
from datetime import datetime
s1 = '10:33:26'
s2 = '11:15:49' # for example
FMT = '%H:%M:%S'
tdelta = datetime.strptime(s2, FMT) - datetime.strptime(s1, FMT)
That gets you a timedelta object that contains the difference between the two times. You can do whatever you want with that, e.g. converting it to seconds or adding it to another datetime.
This will return a negative result if the end time is earlier than the start time, for example s1 = 12:00:00 and s2 = 05:00:00. If you want the code to assume the interval crosses midnight in this case (i.e. it should assume the end time is never earlier than the start time), you can add the following lines to the above code:
if tdelta.days < 0:
tdelta = timedelta(
days=0,
seconds=tdelta.seconds,
microseconds=tdelta.microseconds
)
(of course you need to include from datetime import timedelta somewhere). Thanks to J.F. Sebastian for pointing out this use case.
Try this -- it's efficient for timing short-term events. If something takes more than an hour, then the final display probably will want some friendly formatting.
import time
start = time.time()
time.sleep(10) # or do something more productive
done = time.time()
elapsed = done - start
print(elapsed)
The time difference is returned as the number of elapsed seconds.
Here's a solution that supports finding the difference even if the end time is less than the start time (over midnight interval) such as 23:55:00-00:25:00 (a half an hour duration):
#!/usr/bin/env python
from datetime import datetime, time as datetime_time, timedelta
def time_diff(start, end):
if isinstance(start, datetime_time): # convert to datetime
assert isinstance(end, datetime_time)
start, end = [datetime.combine(datetime.min, t) for t in [start, end]]
if start <= end: # e.g., 10:33:26-11:15:49
return end - start
else: # end < start e.g., 23:55:00-00:25:00
end += timedelta(1) # +day
assert end > start
return end - start
for time_range in ['10:33:26-11:15:49', '23:55:00-00:25:00']:
s, e = [datetime.strptime(t, '%H:%M:%S') for t in time_range.split('-')]
print(time_diff(s, e))
assert time_diff(s, e) == time_diff(s.time(), e.time())
Output
0:42:23
0:30:00
time_diff() returns a timedelta object that you can pass (as a part of the sequence) to a mean() function directly e.g.:
#!/usr/bin/env python
from datetime import timedelta
def mean(data, start=timedelta(0)):
"""Find arithmetic average."""
return sum(data, start) / len(data)
data = [timedelta(minutes=42, seconds=23), # 0:42:23
timedelta(minutes=30)] # 0:30:00
print(repr(mean(data)))
# -> datetime.timedelta(0, 2171, 500000) # days, seconds, microseconds
The mean() result is also timedelta() object that you can convert to seconds (td.total_seconds() method (since Python 2.7)), hours (td / timedelta(hours=1) (Python 3)), etc.
This site says to try:
import datetime as dt
start="09:35:23"
end="10:23:00"
start_dt = dt.datetime.strptime(start, '%H:%M:%S')
end_dt = dt.datetime.strptime(end, '%H:%M:%S')
diff = (end_dt - start_dt)
diff.seconds/60
This forum uses time.mktime()
Structure that represent time difference in Python is called timedelta. If you have start_time and end_time as datetime types you can calculate the difference using - operator like:
diff = end_time - start_time
you should do this before converting to particualr string format (eg. before start_time.strftime(...)). In case you have already string representation you need to convert it back to time/datetime by using strptime method.
I like how this guy does it — https://amalgjose.com/2015/02/19/python-code-for-calculating-the-difference-between-two-time-stamps.
Not sure if it has some cons.
But looks neat for me :)
from datetime import datetime
from dateutil.relativedelta import relativedelta
t_a = datetime.now()
t_b = datetime.now()
def diff(t_a, t_b):
t_diff = relativedelta(t_b, t_a) # later/end time comes first!
return '{h}h {m}m {s}s'.format(h=t_diff.hours, m=t_diff.minutes, s=t_diff.seconds)
Regarding to the question you still need to use datetime.strptime() as others said earlier.
Try this
import datetime
import time
start_time = datetime.datetime.now().time().strftime('%H:%M:%S')
time.sleep(5)
end_time = datetime.datetime.now().time().strftime('%H:%M:%S')
total_time=(datetime.datetime.strptime(end_time,'%H:%M:%S') - datetime.datetime.strptime(start_time,'%H:%M:%S'))
print total_time
OUTPUT :
0:00:05
import datetime as dt
from dateutil.relativedelta import relativedelta
start = "09:35:23"
end = "10:23:00"
start_dt = dt.datetime.strptime(start, "%H:%M:%S")
end_dt = dt.datetime.strptime(end, "%H:%M:%S")
timedelta_obj = relativedelta(start_dt, end_dt)
print(
timedelta_obj.years,
timedelta_obj.months,
timedelta_obj.days,
timedelta_obj.hours,
timedelta_obj.minutes,
timedelta_obj.seconds,
)
result:
0 0 0 0 -47 -37
Both time and datetime have a date component.
Normally if you are just dealing with the time part you'd supply a default date. If you are just interested in the difference and know that both times are on the same day then construct a datetime for each with the day set to today and subtract the start from the stop time to get the interval (timedelta).
Take a look at the datetime module and the timedelta objects. You should end up constructing a datetime object for the start and stop times, and when you subtract them, you get a timedelta.
you can use pendulum:
import pendulum
t1 = pendulum.parse("10:33:26")
t2 = pendulum.parse("10:43:36")
period = t2 - t1
print(period.seconds)
would output:
610
import datetime
day = int(input("day[1,2,3,..31]: "))
month = int(input("Month[1,2,3,...12]: "))
year = int(input("year[0~2020]: "))
start_date = datetime.date(year, month, day)
day = int(input("day[1,2,3,..31]: "))
month = int(input("Month[1,2,3,...12]: "))
year = int(input("year[0~2020]: "))
end_date = datetime.date(year, month, day)
time_difference = end_date - start_date
age = time_difference.days
print("Total days: " + str(age))
Concise if you are just interested in the time elapsed that is under 24 hours. You can format the output as needed in the return statement :
import datetime
def elapsed_interval(start,end):
elapsed = end - start
min,secs=divmod(elapsed.days * 86400 + elapsed.seconds, 60)
hour, minutes = divmod(min, 60)
return '%.2d:%.2d:%.2d' % (hour,minutes,secs)
if __name__ == '__main__':
time_start=datetime.datetime.now()
""" do your process """
time_end=datetime.datetime.now()
total_time=elapsed_interval(time_start,time_end)
Usually, you have more than one case to deal with and perhaps have it in a pd.DataFrame(data) format. Then:
import pandas as pd
df['duration'] = pd.to_datetime(df['stop time']) - pd.to_datetime(df['start time'])
gives you the time difference without any manual conversion.
Taken from Convert DataFrame column type from string to datetime.
If you are lazy and do not mind the overhead of pandas, then you could do this even for just one entry.
Here is the code if the string contains days also [-1 day 32:43:02]:
print(
(int(time.replace('-', '').split(' ')[0]) * 24) * 60
+ (int(time.split(' ')[-1].split(':')[0]) * 60)
+ int(time.split(' ')[-1].split(':')[1])
)

Categories