7
\$\begingroup\$

Given a string in a 24 hour format the program outputs a 12 hour format. The rules are:

  • The output format must be 'hh:mm a.m.' if it represents before midday and 'hh:mm p.m.' after midday
  • When the hour is less than 10:00 you should not write a 0 before the hour, example: '9:05 a.m.'

For example:

12:30 → 12:30 p.m.
09:00 → 9:00 a.m.
23:15 → 11:15 p.m.

The code:

import datetime
def time_converter(time):
    midday_dt = datetime.datetime.strptime('12:00','%H:%M')
    time_dt = datetime.datetime.strptime(time, '%H:%M')

    if time_dt >= midday_dt:
        if time_dt >= datetime.datetime.strptime('13:00','%H:%M'):
            hours, minutes = clamp_to_twelve(time_dt, midday_dt)
            time = f'{hours}:{minutes}'
        time += ' p.m.'
    else:
        if time_dt < datetime.datetime.strptime('10:00','%H:%M'):
            time = time[1:]
        if is_midnight(time_dt):
            hours, minutes = clamp_to_twelve(time_dt, midday_dt)
            time = f'{hours}:{minutes:02d}'
        time += ' a.m.'
    return time

def clamp_to_twelve(time_dt, midday_dt):
    clamp_dt = time_dt - midday_dt
    minutes, seconds = divmod(clamp_dt.seconds, 60)
    hours, minutes = divmod(minutes, 60)
    return [hours, minutes]

def is_midnight(time_dt):
    return (time_dt >= datetime.datetime.strptime('00:00','%H:%M') and
        time_dt <= datetime.datetime.strptime('00:59','%H:%M'))

if __name__ == '__main__':
    assert time_converter('12:30') == '12:30 p.m.'
    assert time_converter('09:00') == '9:00 a.m.'
    assert time_converter('23:15') == '11:15 p.m.'
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

To help with readability, I'd just import datetime.datetime and then alias it, that way you aren't typing datetime.datetime all over the place:

from datetime import datetime as dt

def time_converter(time):
    midday_dt = dt.strptime('12:00','%H:%M')
    time_dt = dt.strptime(time, '%H:%M')

    if time_dt >= midday_dt:
        if time_dt >= dt.strptime('13:00','%H:%M'):
            hours, minutes = clamp_to_twelve(time_dt, midday_dt)
            time = f'{hours}:{minutes}'
        time += ' p.m.'
    else:
        if time_dt < dt.strptime('10:00','%H:%M'):
            time = time[1:]
        if is_midnight(time_dt):
            hours, minutes = clamp_to_twelve(time_dt, midday_dt)
            time = f'{hours}:{minutes:02d}'
        time += ' a.m.'
    return time

def clamp_to_twelve(time_dt, midday_dt):
    clamp_dt = time_dt - midday_dt
    minutes, seconds = divmod(clamp_dt.seconds, 60)
    hours, minutes = divmod(minutes, 60)
    return [hours, minutes]

def is_midnight(time_dt):
    return (time_dt >= dt.strptime('00:00','%H:%M') and
        time_dt <= dt.strptime('00:59','%H:%M'))

if __name__ == '__main__':
    assert time_converter('12:30') == '12:30 p.m.'
    assert time_converter('09:00') == '9:00 a.m.'
    assert time_converter('23:15') == '11:15 p.m.'

Next, if you are just unpacking the list returned from clamp_to_twelve, I would just return a tuple:

def clamp_to_twelve(time_dt, midday_dt):
    clamp_dt = time_dt - midday_dt
    minutes, seconds = divmod(clamp_dt.seconds, 60)
    hours, minutes = divmod(minutes, 60)
    return hours, minutes

This saves you from having to construct the list, as well as the additional overhead of over-allocating memory to take into account the mutable list.

As a last optimization, you could refactor your if statements to not be nested. Because you are using if if rather than if elif, all of your statements are executed within the nested blocks:

def time_converter(time):
    midday_dt = dt.strptime('12:00','%H:%M')
    time_dt = dt.strptime(time, '%H:%M')

    if time_dt >= dt.strptime('13:00', '%H:%M'):
        hours, minutes = clamp_to_twelve(time_dt, midday_dt)
        time = f'{hours}:{minutes} p.m.'

    elif time_dt > midday_dt:
        time += ' p.m.'

    elif time_dt < dt.strptime('10:00', '%H:%M'):
        time = f'{time[1:]} a.m.'

    elif is_midnight(time_dt):
        hours, minutes = clamp_to_twelve(time_dt, midday_dt)
        time = f'{hours}:{minutes} a.m.'

    else:
        time += ' a.m.'

    return time

The only issue being that the order is quite important, since time_dt > midday_dt and time_dt >= dt.strptime('13:00', '%H:%M') are not mutually exclusive. However, you do get a slight time bump because of the separation of blocks of code:

python -m timeit -s "from file import time_converter, time_converter_2; times = ['12:30', '09:00', '11:15']" '(time_converter(t) for t in times)'
1000000 loops, best of 3: 0.275 usec per loop

python -m timeit -s "from file import time_converter, time_converter_2; times = ['12:30', '09:00', '11:15']" '(time_converter_2(t) for t in times)'
1000000 loops, best of 3: 0.277 usec per loop

Emphasis on slight. Depends on what you find more readable.

\$\endgroup\$

Your Answer

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

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