Как написать генератор time_range

Всем привет! Помогите пожалуйста разобраться. Изучаю генераторы. Стоит задача Написать генератор time_range, который принимает два аргумента, время начала и время окончания: time_start, time_end - кортежи с тремя целыми числами hours, minutes, seconds.

На первой итерации time_range должен возвращать начальное время time_start, на каждой последующей итерации он возвращает предыдущее значение, увеличенное на одну секунду. Время окончания time_end не должно возвращаться.

Время окончания может быть меньше времени начала Пример.

t_range = time_range(time_start=(23, 59, 59),
                     time_end=(0, 0, 3))
next(t_range) == (0, 0, 0)
next(t_range) == (0, 0, 1)
next(t_range) == (0, 0, 2)
next(t_range)
# Error: StopIteration

Мой код

def time_range(time_start: tuple, time_end: tuple) -> tuple:
    h_s, m_s, s_s = list(time_start)
    while time_start != time_end:
        s_s = s_s + 1
        m_s = m_s + 1
        q = h_s, m_s, s_s
        yield tuple(q)
        if h_s == 23:
            h_s = 0

        if m_s == 59:
            m_s = 0
            h_s += 1

        if s_s == 59:
            s_s = 0
        m_s += 1

Код написал но чувствую что написал чушь, да и неработает корректно. Помогите пожалуйста. По возможности комментируя код


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

Автор решения: Стас

Предлагаю следующий алгоритм:

  • Примем момент 00:00:00 дня, которому принадлежит startTime за X
  • Теперь переведем наше время startTime из кортежа в число. Для этого нужно подсчитать количество секунд, прошедших с момента X. Получаем: startTimeSeconds = h * 3600 + m * 60 + s.
  • Также переведем в секунды endTime. Теперь один нюанс: если endTime попал на день, следующий после startTime, то endTimeSeconds окажется меньше startTimeSeconds. Но мы понимаем, что момент endTime произошел через сутки + некоторое время (указанное в endTime) после момента X. Поэтому мы можем просто прибавить сутки (86400 секунд) к endTimeSeconds и исправить проблему.
  • Используем обычный range(startTimeSeconds, endTimeSeconds), переводим каждое его значение из секунд в кортеж и возвращаем из генератора.

Реализация:

def timeRange(startTime: tuple, endTime: tuple) -> tuple:
    def timeToSeconds(time_) -> int:
        h, m, s = time_
        return h * 3600 + m * 60 + s

    def secondsToTime(seconds: int) -> tuple:
        h = seconds // 3600 % 24 #добавил %, чтобы переводить 24 часа в 0 часов
        m = seconds % 3600 // 60
        s = seconds % 60

        return (h, m, s)

    startTimeSeconds = timeToSeconds(startTime)
    endTimeSeconds = timeToSeconds(endTime)

    if endTimeSeconds < startTimeSeconds:
        endTimeSeconds += 86400

    for secs in range(startTimeSeconds, endTimeSeconds):
        yield secondsToTime(secs)
    


for t in timeRange((23, 59, 59), (0, 0, 3)):
    print(t, end="; ")

Вывод:

(23, 59, 59); (0, 0, 0); (0, 0, 1); (0, 0, 2);
→ Ссылка
Автор решения: MillMan
def time_range(time_start: tuple, time_end: tuple) -> tuple:
    h_s, m_s, s_s = list(time_start)

    while time_end != time_start:
        q = h_s, m_s, s_s
        yield tuple(q)

        if h_s == 23 and m_s == 59 and s_s == 59:
            h_s = 0
            m_s = 0
            s_s = -1

        if m_s == 59 and s_s == 59:
            h_s + 1

        if s_s == 59:
            h_s + 1

        else:
            s_s += 1

Вот еще вариант решения предложили. Довольно приммитивный но тоже рабочий

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

Функция next_second выдаёт время - секунду следующую за данной. Обычные счётчики с переносами:

def next_second(time_: tuple) -> tuple:
    h, m, s = time_
    if s < 59:
        return h, m, s + 1  # same day, hour, minute, next second
    if m < 59:
        return h, m + 1, 0  # same day, hour, next minute
    if h < 23:
        return h + 1, 0, 0  # same day, next hour
    return 0, 0, 0          # next day

time_range вызывает её в цикле:

def time_range(time_start: tuple, time_end: tuple) -> tuple:
    t = time_start
    while t != time_end:
        yield t
        t = next_second(t)
→ Ссылка
Автор решения: SergFSM

вы можете упростить свой код используя функцию divmod, примерно так:

def time_range(time_start,time_end):
    while time_start!=time_end:
        yield time_start
        h,m,s = time_start
        cm,s = divmod(s+1,60)
        ch,m = divmod(m+cm,60)
        h = (h+ch)%24
        time_start = (h,m,s)
    
t_range = time_range((23, 59, 59),(0, 0, 3))

>>> [*t_range]  # [(23, 59, 59), (0, 0, 0), (0, 0, 1), (0, 0, 2)]
→ Ссылка