Классы time и timezone модуля datetime. Работа со временем в Python

Экземпляры, создаваемые от класса time модуля datetime, предназначены для хранения времени суток (без даты).

Вызов конструктора time без передачи аргументов создает объект времени полуночи (начала суток), а не текущего момента.

В конструктор можно передать час, минуту, секунду и микросекунду (одна миллионная секунды) фиксируемого момента. Все аргументы являются опциональными.

from datetime import time
 
a = time(10, 30)
print(a.strftime("%H:%M"))  # 10:30
 
b = time(minute=3, second=37)
print(b)  # 00:03:37
 
c = time(microsecond=830_000)
print(c.microsecond)  # 830000
print(c.__repr__())  # datetime.time(0, 0, 0, 830000)


Класс time модуля datetime не выполняет автоматическое преобразование значений, превышающих 59 минут в часы и секунд в минуты, больше 23 часа – в сутки, а значение в один миллион микросекунд – в секунду. Поэтому числовые параметры hour, minute, second, microsecond имеют соответствующие им ограничения.

По аналогии с date, объект time можно получить из строки соответствующего ISO-формата, используя классовый метод fromisoformat.

from datetime import time
 
a = time.fromisoformat("20:18:30")
print("A:", a)
print(repr(a))
 
b = time.fromisoformat("00:02:09:004000")
print("\nB:", b)
print(repr(b))
 
c = time.fromisoformat("11:00+03:00")
print("\nC:", c)
print(repr(c))

Результат выполнения:

A: 20:18:30
datetime.time(20, 18, 30)

B: 00:02:09.004000
datetime.time(0, 2, 9, 4000)

C: 11:00:00+03:00
datetime.time(11, 0, tzinfo=datetime.timezone(datetime.timedelta(seconds=10800)))

В примере при создании третьего объекта мы указываем смещение времени от UTC (всемирного времени – это примерно то же самое, что по Гринвичу – GMT). При этом у объекта свойство tzinfo оказывается связанным с экземпляром timezone.

Хотя в литерале "11:00+03:00" пишется плюс, если в часовом поясе, отстоящем от UTC на три часа на восток, 11 часов утра, то в этот самый момент в часовом поясе, где непосредственно действует UTC, будет 8 часов утра, то есть на три часа меньше. Если переместиться от UTC на один часовой пояс на запад, там будет 7 часов утра: "07:00-01:00".

from datetime import time
 
utc = time.fromisoformat("08:00+00:00")
east3 = time.fromisoformat("11:00+03:00")
west1 = time.fromisoformat("07:00-01:00")
 
print(east3 == utc)  # True
print(east3 == west1)  # True

Из примера мы видим, что в Python объекты времени можно сравнивать между собой. Однако лучше этого не делать, если одним операндом является "осведомленное" (aware) время, а другим – "наивное" (naive), то есть время без часового пояса. В таких случаях оператор == всегда возвращает False, != всегда True, а сравнения >, <, >=, <= недопустимы (выбрасывается TypeError). Поэтому следует сравнивать либо только экземпляры времени с часовым поясом, либо только без него.

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

Вернемся к получению объекта через конструктор и рассмотрим параметр tzinfo, которому присваивается экземпляр timezone, который создается через передачу в его конструктор экземпляра timedelta.

from datetime import time, timezone, timedelta
 
tz = timezone(timedelta(hours=10, minutes=50))
a = time(12, 35, 28, tzinfo=tz)
 
b = time(11, 40, tzinfo=timezone(timedelta(hours=-5)))
 
print(a)  # 12:35:28+10:50
print(b)  # 11:40:00-05:00

В данном случае на таймдельту накладываются ограничения. Задаваемый промежуток времени должен быть по модулю меньше длины одних суток.

С помощью метода utcoffset можно получить смещение времени относительно UTC. Если быть точнее, метод просто возвращает ссылку на объект таймдельты.

from datetime import time, timezone, timedelta
 
a = time(12, tzinfo=timezone(timedelta(hours=3)))
b = time(11, tzinfo=timezone(timedelta(hours=-1)))
 
a_offset = a.utcoffset()
b_offset = b.utcoffset()
 
print(a_offset)
print(a_offset.__repr__(), end="\n\n")
 
print(b_offset)
print(b_offset.__repr__())
 
print("\nРазница в часовых поясах:")
print(abs(b_offset - a_offset))

Результат выполнения:

3:00:00
datetime.timedelta(seconds=10800)

-1 day, 23:00:00
datetime.timedelta(days=-1, seconds=82800)

Разница в часовых поясах:
4:00:00

Такое странное представление смещения от UTC на 1 час меньше (минус 1 день плюс 23 часа) связано с внутренним устройством объектов timedelta. У них нет свойства hours, поэтому часы конвертируются в секунды. Кроме того свойство seconds не может хранить отрицательных значений.

Узнать смещение можно не только через метод utcoffset экземпляра time. Также можно обратиться к полю tzinfo этого же экземпляра. Однако следует иметь в виду, что последнее связано с экземпляром timezone, который имеет свой метод utcoffset. И в данном случае в него надо передавать None.

from datetime import time, timezone, timedelta
 
a = time(12, tzinfo=timezone(timedelta(hours=3)))
print(a.tzinfo)  # UTC+03:00
 
offset = a.tzinfo.utcoffset(None)  # экземпляр timedelta
print(offset)  # 3:00:00
 
zn = a.tzinfo.tzname(None)  # экзепляр str
print(zn)  # UTC+03:00

Метод tzname есть и у time. Он также возвращает строку в формате UTC±HH:MM, если только не было задано произвольное наименование часового пояса.

from datetime import time, timezone, timedelta
 
a = time(12, tzinfo=timezone(timedelta(hours=3), name="Moscow"))
b = time(8, tzinfo=timezone(timedelta(hours=0)))
 
print(a.tzname())  # Moscow
print(b.tzname())  # UTC

В мире существуют не только разные часовые пояса. Также в ряде стран переходят на так называемое летнее время (daylight saving time – DST), когда в течение полугода к стандартному смещению часового пояса от UTC добавляется еще один час. Обычно это делают в последнее воскресенье марта, а возвращаются к стандартному поясному времени в последнее воскресенье октября.

Класс timezone не позволяет учитывать DST. Однако на самом деле не обязательно передавать параметру tzinfo объект типа timezone. Самое главное передавать туда экземпляр, созданный от любого подкласса класса tzinfo. Класс timezone является одним из подклассов этого класса.

Другими словами, вы можете написать свой класс, который будет работать с DST.

Обратим внимание, что есть параметр tzinfo конструктора time, есть соответствующее ему свойство объекта, а есть абстрактный класс tzinfo модуля datetime.

При создании экземпляров от datetime не обязательно разрабатывать свой подкласс от tzinfo, чтобы учитывать не только часовые пояса, но и переход на летнее время. В Python есть готовое решение в виде модуля zoneinfo, который подключается к внешней базе данных часовых поясов (есть в системе или устанавливается отдельно). Класс ZoneInfo из модуля zoneinfo является наследником класса tzinfo из модуля datetime.

Атрибуты класса time (подчеркнуты не упомянутые в уроке):


Даты и время в Python. Курс




Все разделы сайта