6
\$\begingroup\$

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:

  1. 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.

  2. 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.

  3. 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()
```
Stephen Rauch
4,31412 gold badges24 silver badges36 bronze badges
asked Jun 20, 2020 at 23:40
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

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

answered Jun 21, 2020 at 13:49
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.