Классы 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
(подчеркнуты не упомянутые в уроке):
- данные, хранимые в классе (свойства класса):
min
,max
,resolution
- методы класса:
fromisoformat
- свойства экземпляра:
hour
,minute
,second
,microsecond
,tzinfo
,fold
- методы экземпляра:
replace
,isoformat
,strftime
,utcoffset
,dst
,tzname