Time residual function in Python - python

I have 2 fields in the database: confirmed_at and preparation_time
I would like to get two simple functions that should return:
(a) the time residual
and
(b) the % of time remaining.
This is my logic which I am trying to achieve:
when order is confirmed_at I update the database with datetime.now() and preparation_time is indicated in minutes and stored as integer (for example 5 min is stored as 5)
completed_time is confirmed_at + preparation_time
time_remaining is completed_time - now()
order status is completed_time / now() * 100
These are my functions but I cannot make them work:
def get_remaining_time(self):
start_time = datetime(self.confirmed_at)
end_time = (start_time + datetime.timedelta(0,self.preparation_time*60)).time() # days, seconds.
return end_time - datetime.now()
def get_order_status(self):
end_time = (datetime(self.confirmed_at) + datetime.timedelta(0,self.preparation_time*60)).time()
return end_time / datetime.now() * 100

What kind of type returned from functions? If it's timedelta - just turn it into date.

So here it is the solution that I've managed to work out
def get_remaining_time(self):
start_time = self.confirmed_at
end_time = start_time + timedelta(
0, self.preparation_time * 60
) # days, seconds.
return end_time - timezone.now()
def get_time_left(self):
left = self.get_remaining_time()
sec = int(left.total_seconds())
if sec > 60:
return "{} minutes".format(int(sec / 60))
elif sec < 60 and sec > 0:
return "{} seconds".format(sec)
else:
return 0
def get_order_status(self):
left = int(self.get_remaining_time().total_seconds())
if left < 0:
return 100
else:
return round((1 - left / (self.preparation_time * 60)) * 100)

Related

Unexpected millisecond offsets when doing subtraction with datetime and timedelta objects

I have this class implemented in python on which i am performing some unitests
from datetime import datetime, timedelta
class FreeTime:
"""Range in a timeframe where a task can be located"""
def __init__(self, start: datetime, end: datetime) -> None:
self.start = start
""" start of the freetime """
self.end = end
""" end of the freetime """
self.duration = end - start
""" duration of the freetime"""
def set_start(self, start: datetime):
self.start = start
self.duration = self.end - self.start
def set_duration(self, duration: timedelta):
self.duration = duration
self.start = self.end - self.duration
def __repr__(self) -> str:
return f"FreeTime - {self.start} -> {self.end} - {self.duration}"
The test looks like this:
from datetime import datetime, timedelta
def test_FreeTime():
"""test the FreeTime class"""
start = datetime.now()
end = start + timedelta(hours=4.0)
free_time = FreeTime(start=start, end=end)
assert free_time.start == start
assert free_time.end == end
assert free_time.duration == end - start
start_2 = datetime.now() + timedelta(hours=1.0)
free_time.set_start(start_2)
assert free_time.start == start_2
assert free_time.duration == end - start_2
delta = timedelta(hours=3.0)
free_time.set_duration(delta)
assert free_time.duration == timedelta(hours=3.0)
assert free_time.start == end - delta
assert str(free_time) == f"FreeTime - {start_2} -> {end} - {delta}"
Strangely when I run the test I get some variant of this assertion error, (some variant meaning the millisecond difference):
assert str(free_time) == f"FreeTime - {start_2} -> {end} - {delta}"
E AssertionError: assert 'FreeTime - 2...544 - 3:00:00' == 'FreeTime - 2...544 - 3:00:00'
E - FreeTime - 2023-01-04 21:54:27.421567 -> 2023-01-05 00:54:27.421544 - 3:00:00
E ? ^^
E + FreeTime - 2023-01-04 21:54:27.421544 -> 2023-01-05 00:54:27.421544 - 3:00:00
E ? ^^
However I dont get how or where the difference in millisecond comes from, since all previous assertion tests pass wiht out errors. It alwasy failes on this assertion but the offset is different every time.
I allready tried instantiatiating all timedelata objects whith floats instead of int, but this did not have any effect.
I try to untangle your test logic:
start = t1 = now()
end = t1 + 4h
free_time.start = start = t1
free_time.end = end = t1 + 4h
start2 = t2 = now() + 1h = t1 + delta_t + 1h
free_time.start = start2 = t2 = t1 + delta_t + 1h
free_time.duration = end - start2 = t1 + 4h - t2
free_time.duration = 3h
free_time.start = free_time.end - 3h = end - 3h = t1 + 1h
The end result is:
free_time.start = t1 + 1h
free_time.end = t1 + 4h
free_time.duration = 3h
And your assertion is that
free_time.start == start2 == t2 == t1 + delta_t + 1h
free_time.end == end == t1 + 4h
free_time.duration == 3h
The error in your logic is that you assume the two calls to datetime.now() result in the same value. They do not. Python is not infinitely fast and the timestamp resolution is sufficiently small that both result in different values, thus resulting in the time difference which I put in the equations as delta_t
Yes - Based on #homer512 's input, change your test class to be
from datetime import datetime, timedelta
def test_FreeTime():
"""test the FreeTime class"""
start = datetime.now()
end = start + timedelta(hours=4.0)
free_time = FreeTime(start=start, end=end)
assert free_time.start == start
assert free_time.end == end
assert free_time.duration == end - start
start_2 = start + timedelta(hours=1.0)
free_time.set_start(start_2)
assert free_time.start == start_2
assert free_time.duration == end - start_2
delta = timedelta(hours=3.0)
free_time.set_duration(delta)
assert free_time.duration == timedelta(hours=3.0)
assert free_time.start == end - delta
assert str(free_time) == f"FreeTime - {start_2} -> {end} - {delta}"

Problem with showing elapsed time in python

I have a problem with function time.time().
I've written a code, which has 3 different hash functions and then it counts how long does they execute.
start_time = time.time()
arr.add(Book1, 1)
end_time = time.time()
elapsed_time = start_time - end_time
print(elapsed_time)
When I execute this in pycharm/IDLE/Visual it shows 0. When I do this in online compiler (https://www.programiz.com/python-programming/online-compiler/) it shows a good result. Why is that?
Here is the full code if needed.
import time
class Ksiazka:
def __init__(self, nazwa, autor, wydawca, rok, strony):
self.nazwa = nazwa
self.autor = autor
self.wydawca = wydawca
self.rok = rok
self.strony = strony
def hash_1(self):
h = 0
for char in self.nazwa:
h += ord(char)
return h
def hash_2(self):
h = 0
for char in self.autor:
h += ord(char)
return h
def hash_3(self):
h = self.strony + self.rok
return h
class HashTable:
def __init__(self):
self.size = 6
self.arr = [None for i in range(self.size)]
def add(self, key, c):
if c == 1:
h = Ksiazka.hash_1(key) % self.size
print("Hash 1: ", h)
if c == 2:
h = Ksiazka.hash_2(key) % self.size
print("Hash 2: ", h)
if c == 3:
h = Ksiazka.hash_3(key) % self.size
print("Hash 3: ", h)
self.arr[h] = key
arr = HashTable()
Book1 = Ksiazka("Harry Potter", "J.K Rowling", "foo", 1990, 700)
start_time = time.time()
arr.add(Book1, 1)
end_time = time.time()
elapsed_time = end_time - start_time
print(elapsed_time)
start_time = time.time()
arr.add(Book1, 2)
end_time = time.time()
elapsed_time = end_time - start_time
print(elapsed_time)
start_time = time.time()
arr.add(Book1, 3)
end_time = time.time()
elapsed_time = end_time - start_time
print(elapsed_time)
I looks like 0 might just be a return value for successful script execution. You need to add a print statement to show anything. Also you might want to change the order of the subtraction:
start_time = time.time()
arr.add(Book1, 1)
end_time = time.time()
elapsed_time = end_time - start_time
print(elapsed_time)
Edit b/c of updated questions:
If it still shows 0, it might just happen, that your add operation is extremely fast. In that case, try averaging over several runs, i.e. instead of your add operation use a version like this:
start_time = time.time()
for _ in range(10**6):
arr.add(Book1, 1)
end_time = time.time()
elapsed_time = end_time - start_time
print(elapsed_time) # indicates the average microseconds for a single run
The documentation for time.time says:
Note that even though the time is always returned as a floating point number, not all systems provide time with a better precision than 1 second. While this function normally returns non-decreasing values, it can return a lower value than a previous call if the system clock has been set back between the two calls.
So, depending on your OS, anything that is faster than 1 second might be displayed as a difference of 0.
I suggest you use time.perf_counter instead:
Return the value (in fractional seconds) of a performance counter, i.e. a clock with the highest available resolution to measure a short duration.

what does the ```time.perf_counter()``` command do?

I do not understand what the time.perf_counter () command does.
Here is the code which includes the command time.perf_counter()
import random
num_nums = 100
start_time = time.perf_counter()
numbers = str(random.randint(1,100))
for i in range(num_nums):
num = random.randint(1,100)
numbers += ',' + str(num)
end_time = time.perf_counter()
td1 = end_time - start_time
start_time = time.perf_counter()
numbers=[]
for i in range(num_nums):
num = random.randint(1,100)
numbers.append(str(num))
numbers = ', '.join(numbers)
end_time = time.perf_counter()
td2 = end_time - start_time
start_time = time.perf_counter()
numbers = [str(random.randint(1,100)) for i in range(1,num_nums)]
numbers = ', '.join(numbers)
end_time = time.perf_counter()
td3 = end_time - start_time
print('''Number of numbers: {:,}
Time Delta 1: {}
Time Delta 2: {}
Time Delta 3: {}'''.format(num_nums, td1, td2, td3))
An here is the result
Time Delta 1: 0.0003232999999909225
Time Delta 2: 0.00016150000010384247
Time Delta 3: 0.0003734999997959676```
Based on the definition here: https://docs.python.org/3/library/time.html#time.perf_counter
The call is gathering the amount of time that has taken place between 2 consecutive calls of time.perf_counter and will be with extreme precision.

Python checking timestamps with if statements not working correctly

I have these if statements that should be checking timestamps. The last if statement doesn't seem to work. These timestamp values should show retrieved timestamp is greater than current - two months, but that doesn't happen. When ran with the following values the last if statement gets triggered.
as requested:
order date 2018-12-17T16:58:00-06:00
values:
one month 2592000
two months 5184000
current timestamp 1545247709.1553745
retrieved timestamp 1545026400
current - two months 1540063709.1553745
one month 259200 <class 'int'>
two months 5184000 <class 'int'>
current timestamp 1545252986.085405 <class 'float'>
retrieved timestamp 1545026400 <class 'int'>
if order_count > 1:
for result in results['orders']:
order_status_info= self_api.which_api('order_statuses/%d' % result['order_status_id'])
for customer_blocked_reason in customer_blocked_reasons:
if customer_blocked_reason in order_status_info['name']:
customer_is_blocked = True
order_id = 0
order_date = result['ordered_at']
two_month_seconds = (3600 * 24) * 60
one_month_seconds = (3600 * 24) * 30
stripped_date = order_date[:order_date.find("T")]
current_timestamp = time.time()
retrieved_timestamp = int(datetime.datetime.strptime(stripped_date, '%Y-%m-%d').strftime("%s"))
print("one month", one_month_seconds)
print("two months", two_month_seconds)
print("current timestamp", current_timestamp)
print("retrieved timestamp", retrieved_timestamp)
print("current - two months", current_timestamp - two_month_seconds)
if retrieved_timestamp > (current_timestamp - one_month_seconds) and not customer_is_blocked:
status['success'] = 1
status['message'] = "Customer Verified with orders older than 30 days and no blocking reasons"
break
elif customer_is_blocked:
status_change_result = self_order.update_status(order_statuses['order_hold_manager_review'])
status['success'] = 1
status['message'] = "Changed order status to Order Hold - Manager Review"
break
elif retrieved_timestamp < (current_timestamp - two_month_seconds):
status['success'] = 0
status['message'] = "There is more than 1 order, and none are greater than 60 days, we need to check manually"
The 'break' statements shouldn't be outside of a loop. But normally the interpreter would catch this. So there must be more code involved, maybe the problem is there. I copied the logic here and assigned the given values to the variables and I get the first option if customer_is_blocked == False and the second option if it is False
two_month_seconds = (3600 * 24) * 60
one_month_seconds = (3600 * 24) * 30
current_timestamp = 1545247709.1553745
retrieved_timestamp = 1545026400
customer_is_blocked = True
if retrieved_timestamp > (current_timestamp - one_month_seconds) and not customer_is_blocked:
print(1)
elif customer_is_blocked:
print(2)
elif retrieved_timestamp < (current_timestamp - two_month_seconds):
print(3)
Did you assign 'False' or 'false' (string) to customer_is_blocked instead of False?
I just tested your code and, after assigning manually to each variable, this seems to be working. I didn't changed anything except replace strftime("%s") with timestamp() because I was getting an error: ValueError: Invalid format string.
import datetime
import time
order_id = 0
# Manually assign this
order_date = "2018-12-17T16:58:00-06:00"
customer_is_blocked = False
two_month_seconds = (3600 * 24) * 60
one_month_seconds = (3600 * 24) * 30
stripped_date = order_date[:order_date.find("T")]
current_timestamp = time.time()
# This is the only change I did: strftime("%s") => timestamp()
retrieved_timestamp = int(datetime.datetime.strptime(stripped_date, "%Y-%m-%d").timestamp())
print("one month", one_month_seconds)
print("two months", two_month_seconds)
print("current timestamp", current_timestamp)
print("retrieved timestamp", retrieved_timestamp)
print("current - two months", current_timestamp - two_month_seconds)
if retrieved_timestamp > (current_timestamp - one_month_seconds) and not customer_is_blocked:
print(1)
elif customer_is_blocked:
print(2)
elif retrieved_timestamp < (current_timestamp - two_month_seconds):
print(3)
With the value for order_date you provided, the code above prints 1 if customer_is_blocked == False, and 2 if customer_is_blocked == True.
Let me know if this worked for you!

Adjusting methods within a class

I am having issues with properly implementing the following:
Remove __hours and __minutes.
Adjust the implementation of the mutator and accessor methods. For
example, for __init__(hr, min, sec), validate the values, then set
__seconds = hr * 60 * 60 + min * 60 + sec. This will store the time as seconds. Adjust all the methods to work with __seconds. getSecond()
can use __seconds mod 60 to return only the seconds in the time. Test
all the methods to make sure they still work. (mod is modulus, the
remainder after a division.)
I have used this in my code self.setSecond = (hour * 60 * 60) + (minute * 60 + second)
To properly represent hour, minute and seconds to just seconds I am however having issues with implementing the rest of the program. I'm not sure if I should be printing out just seconds? Also when I change my getSecond() function to return % 60 it is not doing so. I'm assuming because I'm not properly calling it?
Here is my code thus far:
class Clock(object):
def __init__(self, hour, minute, second):
self.setHour(hour)
self.setMinute(minute)
self.setSecond = (hour * 60 * 60) + (minute * 60 + second)
def setHour(self, hour):
self.__hour = hour
if self.__hour > 23:
self.__hour = 0
elif self.__hour < 0:
self.__hour = 0
def getHour(self):
return self.__hour
def setMinute(self, minute):
self.__minute = minute
if self.__minute > 60:
self.__minute = 0
elif self.__minute < 0:
self.__minute = 0
def getMinute(self):
return self.__minute
def setSecond(self, second):
self.__second = second
if self.__second > 60:
self.__second = 0
elif self.__second < 0:
self.__second = 0
def getSecond(self):
return self.__second
def __str__(self):
if self.__hour > 11:
return 'The Time is {}:{}:{} PM'.format(self.__hour, self.__minute, self.__second)
else:
return 'The Time is {}:{}:{} AM'.format(self.__hour, self.__minute, self.setSecond)
stopwatch = Clock(3, 2, 1)
print(stopwatch)
Note: I am aware that this code isn't very pythonic but that is how I was told to keep it (I apologize).
There are 6 changes required primarily:
You don't need to maintain hours and minutes in the class. You maintain only the seconds. So remove the setHour and setMinute methods.
Because you don't maintain the hours and minutes, the getHour and getMinute methods should do the required calculation.
def getHour(self):
return int(self.__second / 3600)
def getMinute(self):
return int(self.__second / 60) % 60
In the init function, call to self.setSecond is not correct. It should be:
def __init__(self, hour, minute, second):
if hour > 23 or hour < 0:
hour = 0
if minute > 60 or minute < 0:
minute = 0
if second > 60 or second < 0:
second = 0
self.setSecond((hour * 60 * 60) + (minute * 60 + second))
And although you mentioned, your getSecond() method does not do a seconds % 60. It should be:
def getSecond(self):
return self.__second % 60
In method def __str__, instead of directly accessing the hours, minutes, seconds, use the accessor methods:
def __str__(self):
if self.getHour() > 11:
return 'The Time is {}:{}:{} PM'.format(self.getHour(), self.getMinute(), self.getSecond())
else:
return 'The Time is {}:{}:{} AM'.format(self.getHour(), self.getMinute(), self.getSecond())
The setSeconds method should not have the check for > 60 because now you will be storing a large number here:
def setSecond(self, second):
self.__second = second
if self.__second < 0:
self.__second = 0

Categories