Ломается расчет времени

Моя задача высчитать время из секунд
Я написал такой код:

def pretty_time(raw):
    if raw % 60 != raw:
        s = raw % 60
        m = raw // 60
        if m % 60 != m:
            h = s // 60
            m %= 60
            s = m % 60
            pretty = f'{h}ч {m}м {s}с'
        else:
            pretty = f'{m}мин, {s} сек'
    else:
        pretty = f'{raw} сек'
    return pretty

На вид все должно работать правильно?
Так и есть, но после проверки оказалось что...

if __name__ == '__main__':
    for _ in range(10):
        print(pretty_time(int(input(''))))

5
5 сек

59
59 сек

60
1мин, 0 сек

700
11мин, 40 сек

7777
0ч 9м 9с

3600
0ч 0м 0с

86400
0ч 0м 0с

1
1 сек

555
9мин, 15 сек

10000
0ч 46м 46с

Не понимаю, как так могло выйти?

Python 3.10rc2


Ответы (3 шт):

Автор решения: Эникейщик

Ошибка в алгоритме, конечно же!

def pretty_time(raw):
    if raw % 60 != raw:
        s = raw % 60 # s всегда меньше 60!!!
        m = raw // 60
        if m % 60 != m:
            h = s // 60 # если число меньше 60 разделить на 60, то целая часть всегда будет 0

Т.е. мало того, что количество часов вычисляется не из общего количества секунд, а из того количества секунд, которое не уместилось в целую минуту, так еще и само это вычисление происходит неправильно.

→ Ссылка
Автор решения: GrAnd

Я вижу 2 ошибки:

  • В коде h = s // 60 - а надо h = m // 60. Часы ведь от минут вычисляются.
  • И второй раз s = m % 60 испортит вам секунды. Эту (второй раз встречающуюся) строку вообще надо выкинуть из кода.

Итого вся функция должна выглядеть как-то так:

def pretty_time(raw):
    if raw >= 60:
        s = raw % 60
        m = raw // 60
        if m >= 60:
            h = m // 60
            m %= 60
            pretty = f'{h}ч {m}м {s}с'
        else:
            pretty = f'{m}мин, {s}сек'
    else:
        pretty = f'{raw} сек'
    return pretty

Если не нужно выводить элементы времени с нулевыми значениями, то можно вот так:

def pretty_time(raw):
    if not raw: return '0 сек'
    time_values = []
    for div,text in ((60, 'сек'), (60, 'мин'), (24, 'ч'), (raw, 'д')):
        v = raw % div
        if v: time_values.append(f'{v} {text}')
        raw //= div
    return ' '.join(time_values[::-1])
→ Ссылка
Автор решения: CrazyElf

Вообще в таких вычислениях лучше не считать сущности отдельно, а попытаться как-то универсально их обрабатывать, тогда заведомо меньше ошибок будет в коде. Например, если обрабатывается время не больше, чем сутки, то можно использовать такой код, поскольку часы, минуты и секунды вычисляются по совершенно одинаковому алгоритму - выводим остаток от деления, потом делим нацело и т.д.:

def pretty_time(raw):
    res = ''
    for t in 'смч':
        res = f'{raw % 60}{t} {res}'
        raw //= 60
        if raw == 0:
            break
    print(res)

pretty_time(59)
pretty_time(555)
pretty_time(10000)

Вывод:

59с 
9м 15с 
2ч 46м 40с 

Но и для бОльших количеств времени можно использовать похожий алгорим, только нужно будет перебирать не только единицы времени, но и одновременно величину делителя для получения этих единиц. Алгоритм чуть усложнится, но останется таким же универсальным.

→ Ссылка