Какой стандартный способ добавить N секунд для datetime.time в Python?

370

Учитывая datetime.timeзначение в Python, есть ли стандартный способ добавить к нему целое число секунд, так что , например , 11:34:59+ 3 = 11:35:02?

Эти очевидные идеи не работают:

>>> datetime.time(11, 34, 59) + 3
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'int'
>>> datetime.time(11, 34, 59) + datetime.timedelta(0, 3)
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'datetime.timedelta'
>>> datetime.time(11, 34, 59) + datetime.time(0, 0, 3)
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'datetime.time'

В конце я написал такие функции:

def add_secs_to_time(timeval, secs_to_add):
    secs = timeval.hour * 3600 + timeval.minute * 60 + timeval.second
    secs += secs_to_add
    return datetime.time(secs // 3600, (secs % 3600) // 60, secs % 60)

Я не могу не думать, что мне не хватает более простого способа сделать это, хотя.

связанные с

Пол Стивенсон
источник
связанная проблема Python: поддержка datetime.time для '+' и '-'
jfs

Ответы:

500

Вы можете использовать полные datetimeпеременные с timedeltaи, указав фиктивную дату, а затем timeпросто получить значение времени.

Например:

import datetime
a = datetime.datetime(100,1,1,11,34,59)
b = a + datetime.timedelta(0,3) # days, seconds, then other fields.
print(a.time())
print(b.time())

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

11:34:59
11:35:02

Вы также можете выбрать более читабельный

b = a + datetime.timedelta(seconds=3)

если вы так склонны.


Если вам нужна функция, которая может сделать это, вы можете посмотреть addSecsниже:

import datetime

def addSecs(tm, secs):
    fulldate = datetime.datetime(100, 1, 1, tm.hour, tm.minute, tm.second)
    fulldate = fulldate + datetime.timedelta(seconds=secs)
    return fulldate.time()

a = datetime.datetime.now().time()
b = addSecs(a, 300)
print(a)
print(b)

Это выводит:

 09:11:55.775695
 09:16:55
paxdiablo
источник
6
Чтобы избежать ошибок переполнения, я бы рекомендовал использовать другую фиктивную дату, например пару лет спустя: datetime (101,1,1,11,34,59). Если вы попытаетесь вычесть большую временную дельту из указанной выше даты, вы получите ошибку «OverflowError: значение даты вне диапазона», поскольку год для объекта datetime не может быть меньше 1
Феликс
2
@pheelicks, сделано, хотя и немного поздно, не совсем быстрое время отклика :-) Так как мне пришлось исправлять еще одну ошибку в моем коде, я решил включить ваше предложение одновременно.
paxdiablo
53

Как уже говорили другие, вы можете просто использовать полные объекты datetime:

from datetime import datetime, date, time, timedelta
sometime = time(8,00) # 8am
later = (datetime.combine(date.today(), sometime) + timedelta(seconds=3)).time()

Тем не менее, я думаю, что стоит объяснить, почему требуются полные объекты даты и времени. Посмотрим, что произойдет, если я добавлю 2 часа до 11 вечера. Какое правильное поведение? Исключение, потому что вы не можете иметь время больше, чем 23:59 вечера? Должен ли он обернуться?

Разные программисты будут ожидать разных вещей, поэтому любой результат, который они выберут, удивит многих. Хуже всего то, что программисты писали код, который отлично работал, когда они сначала тестировали его, а затем ломали его позже, делая что-то неожиданное. Это очень плохо, поэтому вы не можете добавлять объекты timedelta к объектам времени.

Эли Кортрайт
источник
22

Одна маленькая вещь, может добавить ясности, чтобы переопределить значение по умолчанию на секунды

>>> b = a + datetime.timedelta(seconds=3000)
>>> b
datetime.datetime(1, 1, 1, 12, 24, 59)
неокантованный
источник
4
Мне нравится этот. Красиво и понятно с указанным аргументом.
Вигронд
12

Спасибо @Pax Diablo, @bvmou и @Arachnid за предложение использовать полные даты и времени. Если мне нужно принять объекты datetime.time из внешнего источника, то это, кажется, альтернативная add_secs_to_time()функция:

def add_secs_to_time(timeval, secs_to_add):
    dummy_date = datetime.date(1, 1, 1)
    full_datetime = datetime.datetime.combine(dummy_date, timeval)
    added_datetime = full_datetime + datetime.timedelta(seconds=secs_to_add)
    return added_datetime.time()

Этот подробный код может быть сжат до этой строки:

(datetime.datetime.combine(datetime.date(1, 1, 1), timeval) + datetime.timedelta(seconds=secs_to_add)).time()

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

Пол Стивенсон
источник
Спасибо - отличная идея
Anupam
9

Если стоит добавить еще один файл / зависимость в ваш проект, я только что написал крошечный маленький класс, который расширяет datetime.timeвозможности арифметики. Когда вы выходите после полуночи, оно оборачивается вокруг нуля. Теперь «Сколько времени будет через 24 часа» имеет множество угловых случаев, включая переход на летнее время, дополнительные секунды, исторические изменения часового пояса и т. Д. Но иногда вам действительно нужен простой случай, и вот что это будет делать.

Ваш пример будет написан:

>>> import datetime
>>> import nptime
>>> nptime.nptime(11, 34, 59) + datetime.timedelta(0, 3)
nptime(11, 35, 2)

nptimeнаследуется от datetime.time, поэтому любой из этих методов также должен быть пригоден для использования.

Он доступен из PyPi как nptime(« непедантичное время») или на GitHub: https://github.com/tgs/nptime

rescdsk
источник
6

Вы не можете просто добавить число, datetimeпотому что неясно, какая единица измерения используется: секунды, часы, недели ...

Есть timedeltaкласс для манипуляций с датой и временем. datetimeминус datetimeдает timedelta, datetimeплюс timedeltaдает datetime, два datetimeобъекта не могут быть добавлены, хотя два timedeltaмогут.

Создайте timedeltaобъект за сколько секунд вы хотите добавить и добавить его в datetimeобъект:

>>> from datetime import datetime, timedelta
>>> t = datetime.now() + timedelta(seconds=3000)
>>> print(t)
datetime.datetime(2018, 1, 17, 21, 47, 13, 90244)

Существует так же понятие в C ++: std::chrono::duration.

user2387567
источник
4
пожалуйста, ВСЕГДА добавляйте объяснения, новички могут не иметь ни малейшего понятия, что вы делаете здесь.
Герхард Барнард
3

Для полноты, вот способ сделать это arrow(лучше даты и время для Python):

sometime = arrow.now()
abitlater = sometime.shift(seconds=3)
Барт Ван Лоон
источник
1

Попробуйте добавить datetime.datetimeк datetime.timedelta. Если вам нужна только часть времени, вы можете вызвать time()метод для результирующего datetime.datetimeобъекта, чтобы получить его.

Ник Джонсон
источник
0

Старый вопрос, но я решил добавить функцию, которая обрабатывает часовые пояса. Ключевые части передают атрибут datetime.timeобъекта tzinfoв комбинат, а затем используют timetz()вместо time()него фиктивную дату-время. Этот ответ частично вдохновлен другими ответами здесь.

def add_timedelta_to_time(t, td):
    """Add a timedelta object to a time object using a dummy datetime.

    :param t: datetime.time object.
    :param td: datetime.timedelta object.

    :returns: datetime.time object, representing the result of t + td.

    NOTE: Using a gigantic td may result in an overflow. You've been
    warned.
    """
    # Create a dummy date object.
    dummy_date = date(year=100, month=1, day=1)

    # Combine the dummy date with the given time.
    dummy_datetime = datetime.combine(date=dummy_date, time=t, tzinfo=t.tzinfo)

    # Add the timedelta to the dummy datetime.
    new_datetime = dummy_datetime + td

    # Return the resulting time, including timezone information.
    return new_datetime.timetz()

И вот действительно простой класс тестового примера (с использованием встроенного unittest):

import unittest
from datetime import datetime, timezone, timedelta, time

class AddTimedeltaToTimeTestCase(unittest.TestCase):
    """Test add_timedelta_to_time."""

    def test_wraps(self):
        t = time(hour=23, minute=59)
        td = timedelta(minutes=2)
        t_expected = time(hour=0, minute=1)
        t_actual = add_timedelta_to_time(t=t, td=td)
        self.assertEqual(t_expected, t_actual)

    def test_tz(self):
        t = time(hour=4, minute=16, tzinfo=timezone.utc)
        td = timedelta(hours=10, minutes=4)
        t_expected = time(hour=14, minute=20, tzinfo=timezone.utc)
        t_actual = add_timedelta_to_time(t=t, td=td)
        self.assertEqual(t_expected, t_actual)


if __name__ == '__main__':
    unittest.main()
blthayer
источник