Beginner level script for a countdown timer. Would appreciate if you could point the things I could do to be more efficient.
Basically, I would want to check if the current day is either a "workday"(Mon - Wed) or a "freeday"(Thur-Sun) and achieve the following:
If it's a workday, and the current hour is a working hour (7:00:00 AM - 5:00:00 PM), then count how many hour(s) remaining till end of the shift (5PM) base on the current time.
If it's a workday, and the current hour is outside of working hours (5:00:01 PM to 6:59:59 AM), say that it is not yet a working hour.
If it's a freeday, count till the next Monday.
I wouldn't want to rely on the if statements if possible, but my understanding level for Python is at this point only. Is there a better way to do it?
Thank you in advance! Happy coding.
import time, datetime
# Add the value to get the next Monday
get_future_monday = {
0: 1,
1: 2,
2: 3,
3: 4
}
def time_state():
# Check what day of the week
# 0 == Monday, 6 == Sunday
check_day = datetime.datetime.today().weekday()
# Get future Monday
future_monday = 6 - check_day
# Check current time
check_time_now = datetime.datetime.now()
# Parse current time
# Get the year, month and day
get_year = check_time_now.year
get_month = check_time_now.month
get_day = check_time_now.day
# Define timespan for each routine
working_hours = datetime.time(7, 0, 0)
after_hours_am = datetime.time(6, 59, 59)
after_hours_pm = datetime.time(17, 0, 1)
if check_day == 0 and check_time_now.time() < after_hours_am: # If Monday and current time not yet 7 AM
print("It's not time yeeeet!")
return datetime.datetime(get_year, get_month, get_day, 7, 0, 0)
elif check_day == 2 and check_time_now.time() > after_hours_pm: # If Wednesday and current time past 5 PM
print("Time to gooooo!")
return datetime.datetime(get_year, get_month, (get_day + get_future_monday[future_monday]), 7, 0, 0)
elif check_day <= 2 and check_time_now.time() < after_hours_am: # If day in Tuesday, Wed and current time not yet 7 AM
print("Get ready to grind...")
return datetime.datetime(get_year, get_month, get_day, 7, 0, 0)
elif check_day <= 2 and check_time_now.time() > after_hours_pm: # If day in Tuesday, Wed and current time not yet 5 PM
print("Free at last!")
return datetime.datetime(get_year, get_month, (get_day + 1), 7, 0, 0)
elif check_day <= 2 and check_time_now.time() >= working_hours and check_time_now.time() <= after_hours_pm: # If day in Mon - Wed and current time not yet 5 PM
print("Focus focus focus!!!")
return datetime.datetime(get_year, get_month, get_day, 17, 0, 0)
else:
print("Playtime foo UwU")
return datetime.datetime(get_year, get_month, (get_day + get_future_monday[future_monday]), 7, 0, 0) # Playtime
def countdown(time_state):
time_state = time_state
clock_tick = time_state - datetime.datetime.now()
days = clock_tick.total_seconds() / 86400
hours, rem = divmod(clock_tick.total_seconds(), 3600)
mins, secs = divmod(rem, 60)
print(f"{int(days):02d}:{int(hours):02d}:{int(mins):02d}:{int(secs):02d}")
time.sleep(1)
def main():
while True:
a = time_state()
countdown(a)
main()
```
1 Answer 1
Variable names
I find the get_
prefixes unnecessary on these:
get_year = check_time_now.year
get_month = check_time_now.month
get_day = check_time_now.day
That makes them look like functions.
Boundaries
This:
working_hours = datetime.time(7, 0, 0)
after_hours_am = datetime.time(6, 59, 59)
should probably just use 7:00 for after_hours_am
. Otherwise, the time 6:59:59 given your current usage will still be considered working hours.
Likewise, this:
check_time_now.time() > after_hours_pm
should use >=
.
An easy convention to follow that makes your boundary checking consistent is to always use intervals that are closed at the beginning and open at the end, i.e. 7:00 <= time < 8:00.
Redundant else
This:
return datetime.datetime(get_year, get_month, get_day, 7, 0, 0)
elif ...
can use an if
instead of an elif
due to the previous return.
Don't repeat yourself
This return:
return datetime.datetime(get_year, get_month, get_day, 7, 0, 0)
shares many commonalities with the other returns in its function. Factor out a new_day
and new_hour
variable assignment, then at the end of your function,
return datetime.datetime(get_year, get_month, new_day, new_hour)
Note that 0 is the default for the remaining parameters.
Timespan division
Trying not to do your own time math, this would be one option that doesn't require knowledge of time position multiples:
days = clock_tick.days
rem = clock_tick - timedelta(days=days)
hours = rem // timedelta(hours=1)
rem -= timedelta(hours=hours)
mins = rem // timedelta(minutes=1)
rem -= timedelta(minutes=mins)
secs = int(rem.total_seconds())
I don't like it, but built-in Python time libraries are kind of bad. Third-party libraries will reduce this to a one-liner format call. This is educational:
https://stackoverflow.com/questions/538666/format-timedelta-to-string