Python Elapsed Time as Days, Hours, Minutes, Seconds - python

I would like to measure the execution time of some piece of code in days, hours, minutes and seconds.
This is what I have so far:
import time
start_time = time.time()
# some code
elapsed = time.strftime("%H:%M:%S", time.gmtime(time.time() - start_time))
print(f"Took: {elapsed}")
The problem is that if the code that I am measuring takes longer than 24h, the time displayed overflows and starts from zero again. I would like something like this:
# Example: 12 hours and 34 minutes should be printed as
> Took: 12:34:00
# Example: 26 hours and 3 minutes should be printed as
> Took: 1:02:03:00

You could use datetime:
from datetime import datetime as dt
start = dt.fromtimestamp(1588432670)
end = dt.now()
elapsed=end-start
print("Took: %02d:%02d:%02d:%02d" % (elapsed.days, elapsed.seconds // 3600, elapsed.seconds // 60 % 60, elapsed.seconds % 60))
Output:
Took: 33:00:21:49

The result of time.gmtime(time.time() - start_time) is not what you seem to think it is. Instead of being a duration of time it is a point in time. Let me explain.
The result of time.time() is the number of seconds since January 1, 1970, 00:00:00 (UTC) at the time of calling. Therefore, the statement time.time() - start_time will produce the number of seconds between the two calls. So far so good. However, the time.gmtime function is interpreting this duration as the number of seconds since January 1, 1970, 00:00:00 (UTC) and formatting the time accordingly. What you are seeing then is the time portion of the date January 1, 1970, 12:34:00 (UTC).
I suggest you either use the datetime.timedelta object and format using that, or as others have suggested, output the duration in seconds or milliseconds.
If you want to format this number yourself, you could use something like this:
def format_duration(duration):
mapping = [
('s', 60),
('m', 60),
('h', 24),
]
duration = int(duration)
result = []
for symbol, max_amount in mapping:
amount = duration % max_amount
result.append(f'{amount}{symbol}')
duration //= max_amount
if duration == 0:
break
if duration:
result.append(f'{duration}d')
return ' '.join(reversed(result))

You should try this:
import time
start_time = time.time()
...
elapsed_time = time.time() - start_time
days = 0
if elapsed_time >= 86400:
days = int(elapsed_time / 86400)
elapsed = time.strftime("%H:%M:%S", time.gmtime(time.time() - start_time))
if days == 0:
print(f"Took: {elapsed}")
else:
print(f"Took: {days}:{eplased}")

Time types can only include hours and less units. You should use datetime instead of time as follows:
from datetime import datetime
start_time = datetime.now()
# some code
elapsed = datetime.now() - start_time)
print(f"Took: {elapsed}")
Example usage of Datetime:
from datetime import datetime
d1 = datetime(2013,9,1,5,5,4)
d2 = datetime(2013,1,13,3,2,1)
result1 = d1-d2
print ('{} between {} and {}'.format(result1, d1, d2))
This produces following output:
231 days, 2:03:03 between 2013-09-01 05:05:04 and 2013-01-13 03:02:01

Try using timeit:
import timeit
timeit.timeit(<callable function>, number = 100)
Here timeit will call callable function number times and give you the average runtime.

Related

Why datetime substraction in Python doesn't subtract days?

I expect that if I substract two datetimes in Python, I'll get datetime with substracted days, weeks, etc...
Here is my sample. What I get are just substracted hours, minutes and seconds. Date variable is taken from database. On type() function returns datetime.datetime.
def elapsed_time(date):
"""
Custom filter that format time to "x time age".
:param date:
:return:
"""
if date is None:
return 'No time given'
now = datetime.datetime.now()
elapsed = (now - date).seconds
if elapsed < 60:
return '{} seconds ago'.format(elapsed)
elif elapsed < 3600:
return '{} minutes ago'.format(int(elapsed / 60))
elif elapsed < 86400:
return '{} hours ago'.format(int(elapsed / 3600))
else:
return '{} days ago'.format((elapsed / 86400))
My current example:
Given datetime is 2017-07-27 01:18:58.398231
Current datetime is 2017-07-31 20:23:36.095440
Result is 19 hours (68677 seconds)
The following line returns only the 'seconds' component of the difference and does not take into account the days/hours/minutes components of it.
elapsed = (now - date).seconds
What you need is to use total_seconds() instead of just seconds since that's what you're trying to compare in subsequent conditions. Use it as follows:
elapsed = (now - date).total_seconds()
The rest of the code remains the same and you will get your desired output.
If you subtract two datetime objects the result will be a timedelta
import datetime as dt
import time
t1 = dt.datetime.now()
time.sleep(4)
t2 = dt.datetime.now()
dt1 = t2 - t1
print(dt1)
print(dt1.total_seconds())
print(type(dt1))
dt2 = t1 - t2
print(dt2.total_seconds())
print(dt2)
print(type(dt2))
If the second timestep was earlier than the first one, the results can be irritating. See negative day in example.
dt.seconds is only a part of the result, you are looking for
timedelta.total_seconds()

Python datetime over one day

import datetime
start_time = datetime.datetime.now()
end_time = datetime.datetime.now()
print (end_time - start_time)
I try to to use datetime to get the execution time.
If it took almost 11 hours, it will show like 11:07:13.215032
If more than 24 hours, how to show the time?
e.g. 35 hours 11minutes 37 seconds
1) 35:11:37
2) 1:11:11:37
Which one will be shown?
Just give it a start_time
import datetime
start_time = datetime.datetime.strptime('1997-01-01 00:00:00', '%Y-%m-%d %H:%M:%S')
end_time = datetime.datetime.now()
print (end_time - start_time)
Output:
7363 days, 17:06:57.965556

Unable to get proper timestamp ranges

I am hoping to generate a range of timestamps between:
18:00 (EST) on October 6th, 2014
and the same time 400 seconds later with an interval size of 2.2 seconds.
Getting the start and end dates:
When I do the following:
start_time = datetime.datetime(year = 2014,
month = 10,
day = 6,
hour = 18,
tzinfo = pytz.timezone('US/Eastern'))
end_time = start_time + datetime.timedelta(seconds=400)
Something seems to fail:
start_time.isoformat() returns '2014-10-06T18:06:40-04:56'
end_time.isoformat() returns '2014-10-06T18:06:40-04:56'
note that the time-zone offset for both timestamps above are: -04:56 (4 hours and 56 minutes) even though EST is 5 hours behind UTC. ?
Getting the time range:
Moving forward, if I try to get a range of timestamps between these two dates every 2.2 seconds (i.e. 2200 ms):
ts = pd.date_range(start=start_time, end=end_time, freq='2200L')
I get:
> ts[0]
Timestamp('2014-10-06 18:56:00-0400', tz='US/Eastern', offset='2200L')
or in other words:
> ts[0].isoformat()
'2014-10-06T18:56:00-04:00'
which also does not make sense (note that the time is 18:56, even though I was asking to get a range between 18:00 and 18:06:40 (i.e. 400 seconds after 18:00)
I got tired of dealing with Python's awkward datetime implementation (particularly with respect to timezones), and have started using crsmithdev.com/arrow. A solution using this lib:
import arrow
start_time = arrow.get(2014, 10, 6, tzinfo='US/Eastern')
end_time = start_time.replace(seconds=400)
print start_time.isoformat()
print end_time.isoformat()
# alternate form
start_time = arrow.get('2014-10-06T18:00:00.000-04:00')
end_time = start_time.replace(seconds=400)
print start_time.isoformat()
print end_time.isoformat()
# get a datetime from an arrow object
start_time_dt = start_time.datetime
import dateutil.parser as parser
import datetime
start_time = parser.parse("06/Oct/2015 18:00 EST")
end_time = start_time + datetime.timedelta(seconds=400)
interval = datetime.timedelta(seconds=2.2)
current_time = start_time
while current_time < end_time:
current_time += interval
print current_time
but I probably dont understand what your issue is

Python time format with three-digit hour

How can I parse the time 004:32:55 into a datetime object? This:
datetime.strptime("004:32:55", "%H:%M:%S")
doesn't work becaush %H expects two digits. Any way to add the third digit?
Three options :
s = "004:32:55"
from datetime import datetime
datetime.strptime(s[1:], "%H:%M:%S") # from second 0 to the end
datetime.strptime(s, "0%H:%M:%S") # add 0 in formatting
from dateutil import parser # use dateutil
parser.parse(s)
There are 24 hours in a day so you can divide and get the modulus to figure out how many days and hours are in the time, then subtract the days, this needs some tweaking will get you started.
s = "04:32:55"
s = s.split(":",1)
hours, days = divmod(int(s[0]),24)
new_time = "{}:{}".format(hours,s[1])
past = datetime.now() - timedelta(days=days)
final = "{} {}".format(past.date().isoformat(),new_time)
print datetime.strptime(final,"%Y-%m-%d %H:%M:%S")
I chose a more pragmatic approach in the end and converted the time stamp to seconds:
hours = (0 if time_string.split(":")[0] == "000" else int(time_string.split(":")[0].lstrip("0")) * 3600)
mins = (0 if time_string.split(":")[1] == "00" else int(time_string.split(":")[1].lstrip("0")) * 60)
secs = (0 if time_string.split(":")[2] == "00" else int(time_string.split(":")[2].lstrip("0")))
return hours + mins + secs
Converting back to hours, minutes, and seconds is easy with datetime.timedelta(seconds=123).
EDIT:
A better solution (thanks to Ben):
hours = int(time_string.split(":")[0]) * 3600
mins = int(time_string.split(":")[1]) * 60
secs = int(time_string.split(":")[2])
return hours + mins + secs

Python, datetime "time gap" percentage

Assume I have these datatime variables:
start_time, end_time, current_time
I would like to know how much time left as percentage by checking current_time and the time delta between start_time and the end_time
IE: Assume the interval is a 24 hours betwen start_time and end_time yet between current_time and end_time, there are 6 hours left to finish, %25 should be left.
How can this be done ?
In Python 2.7.x, time delta has a method total_seconds to achieve this:
import datetime
startTime = datetime.datetime.now() - datetime.timedelta(hours=2)
endTime = datetime.datetime.now() + datetime.timedelta(hours=4)
rest = endTime - datetime.datetime.now()
total = endTime - startTime
print "left: {:.2%}".format(rest.total_seconds()/total.total_seconds())
In Python 3.2, you can apparently divide the time deltas directly (without going through total_seconds).
(This has also been noted in Python 2.6.5: Divide timedelta with timedelta.)
Here's a hackish workaround: compute the total number of microseconds between the two values by using the days, seconds, and microseconds fields. Then divide by the total number of microseconds in the interval.
Possibly simplest:
import time
def t(dt):
return time.mktime(dt.timetuple())
def percent(start_time, end_time, current_time):
total = t(end_time) - t(start_time)
current = t(current_time) - t(start_time)
return (100.0 * current) / total

Categories