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()
```
\$\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

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.