Использование Python datetime для работы с датами и временем

9 мая, 2020

Подписывайся на наш канал в Telegram, чтобы ежедневно совершенствоваться в Python - перейти

Оглавление

  • Программирование с датами и временем
    • Как компьютеры считают время
    • Как сообщить стандартные даты
    • Как должно храниться время в вашей программе
  • Использование модуля даты и времени Python
    • Создание экземпляров Python datetime
    • Использование строк для создания экземпляров Python datetime
  • Начало вашего обратного отсчета PyCon
  • Работа с часовыми поясами
    • Использование dateutil для добавления часовых поясов в Python datetime
    • Сравнение наивного и осведомленного экземпляров Python datetime
  • Улучшение вашего обратного отсчета PyCon
  • Реализация арифметики с Python
  • Завершение вашего обратного отсчета PyCon
    • Использование относительной дельты в вашем обратном отсчете PyCon
    • Отображение даты PyCon в вашем обратном отсчете PyCon
    • Завершение вашего обратного отсчета PyCon
  • Альтернативы Python datetime и dateutil
  • Вывод

Работа с датами и временем является одной из самых больших проблем в программировании. Между работой с часовыми поясами, переходом на летнее время и различными письменными форматами даты может быть сложно отслеживать, на какие дни и время вы ссылаетесь. К счастью, встроенный datetime модуль Python может помочь вам управлять сложным характером дат и времени.

В этом уроке вы узнаете:

  • Почему программирование с датами и временем является такой проблемой
  • Какие функции доступны в модуле Python datetime
  • Как напечатать или прочитать дату и время в определенном формате
  • Как сделать арифметику с датами и временем

Давайте начнем!

Программирование с датами и временем

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

Одним из ярких примеров этого является летнее время . В Соединенных Штатах и ​​Канаде часы переводятся на один час во второе воскресенье марта и на один час в первое воскресенье ноября. Однако это имеет место только с 2007 года . До 2007 года часы были установлены в первое воскресенье апреля и перенесены на последнее воскресенье октября.

Все становится еще сложнее, когда вы рассматриваете часовые пояса . В идеале границы часового пояса должны точно следовать линиям долготы. Однако по историческим и политическим причинам часовые пояса редко бывают прямыми. Часто области, разделенные большими расстояниями, оказываются в одном часовом поясе, а соседние области находятся в разных часовых поясах. Есть некоторые часовые пояса с довольно причудливыми формами .

Как компьютеры считают время

Почти все компьютеры отсчитывают время с момента, названного эпохой Unix . Это произошло 1 января 1970 года в 00:00:00 UTC. UTC обозначает всемирное координированное время и относится к времени на долготе 0 °. UTC часто также называют временем по Гринвичу Гринвичу или GMT. UTC не настроен на летнее время, поэтому он постоянно работает круглосуточно каждый день.

По определению, время Unix проходит с той же скоростью, что и UTC, поэтому шаг в одну секунду в UTC соответствует шагу в одну секунду во времени Unix. Обычно вы можете определить дату и время в UTC для любого данного момента времени с 1 января 1970 года, подсчитав количество секунд с начала эпохи Unix, за исключением високосных секунд . Иногда в UTC добавляются високосные секунды, чтобы учесть замедление вращения Земли, но не добавляются к времени Unix.

Примечание: есть интересная ошибка, связанная со временем Unix. Поскольку многие старые операционные системы являются 32-разрядными, они хранят время Unix в виде 32-разрядного целого числа со знаком.

Это означает, что в 03:14:07 19 января 2038 года целое число переполнится, что приведет к так называемой проблеме 2038 года , или Y2038. Подобно проблеме 2000 года , необходимо исправить Y2038, чтобы избежать катастрофических последствий для критических систем.

Почти все языки программирования, включая Python , включают в себя концепцию времени Unix. Стандартная библиотека Python включает в себя модуль, time который может выводить количество секунд с начала эпохи Unix: >>>

>>> import time
>>> time.time()
1579718137.550164

В этом примере мы импортировали time модуль и выполнили time() для печати времени Unix, или количество секунд ( за исключением високосных секунд) с началом эпохи.

В дополнение к времени Unix компьютерам нужен способ передачи информации о времени пользователям. Как вы видели в последнем примере, время Unix для человека почти невозможно проанализировать. Вместо этого время Unix обычно преобразуется в UTC, которое затем можно преобразовать в местное время, используя смещения часового пояса .

Управление по присвоению номеров в Интернете (IANA) поддерживает базу данных всех значений смещений часовых поясов. IANA также выпускает регулярные обновления, которые включают любые изменения в смещениях часовых поясов. Эта база данных часто входит в состав вашей операционной системы, хотя некоторые приложения могут содержать обновленную копию.

База данных содержит копию всех обозначенных часовых поясов и сколько часов и минут они смещены относительно UTC. Таким образом, зимой, когда переход на летнее время не действует, восточный часовой пояс США имеет смещение -05: 00 или отрицательные пять часов от UTC. Другие регионы имеют разные смещения, которые не могут быть целыми часами. Смещение UTC для Непала, например, составляет +05: 45 или положительное значение составляет пять часов и сорок пять минут от UTC.

Как сообщить стандартные даты

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

Например, в Соединенных Штатах даты обычно пишутся начиная с месяца, затем дня, затем года. Это означает, что 31 января 2020 года записано как 01-31-2020 . Это близко соответствует длинной форме письменной версии даты.

Тем не менее, большинство стран Европы и многих других регионов пишут дату, начиная с дня, затем месяца, затем года. Это означает, что 31 января 2020 года записывается как 31-01-2020 . Эти различия могут вызывать все виды путаницы при общении между культурами.

Чтобы избежать ошибок в коммуникации, Международная организация по стандартизации (ISO) разработала ISO 8601 . В этом стандарте указывается, что все даты должны быть записаны в порядке самых младших данных. Это означает, что форматом является год, месяц, день, час, минута и секунда:

YYYY-MM-DD HH:MM:SS

В этом примере, YYYY представляет собой четыре цифры года, а также MM и DD являются две цифры месяца и дня, начиная с нуля , если это необходимо. После этого, HH и MM, и SS представляют двузначные часы, минуты и секунды, начиная с нуля, если это необходимо.

Преимущество этого формата в том, что дата может быть представлена ​​без двусмысленности. Даты, записанные как DD-MM-YYYY или MM-DD-YYYY могут быть неверно истолкованы, если день является действительным номером месяца. Чуть позже вы увидите, как вы можете использовать формат ISO 8601 с Python datetime.

Как должно храниться время в вашей программе

Большинство разработчиков, которые работали со временем, слышали совет преобразовать местное время в UTC и сохранить это значение для дальнейшего использования. Во многих случаях, особенно когда вы храните даты из прошлого, этой информации достаточно для выполнения любой необходимой арифметики.

Однако проблема может возникнуть, если пользователь вашей программы вводит будущую дату по местному времени. Правила часового пояса и перехода на летнее время меняются довольно часто, как вы видели ранее с изменением летнего времени в США и Канаде в 2007 году. Если правила часового пояса для местоположения вашего пользователя изменятся до введенной ими будущей даты, то UTC не предоставит достаточно информации для преобразования обратно в правильное местное время.

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

В этом случае вам необходимо сохранить местное время, включая часовой пояс, введенное пользователем, а также версию базы данных часовых поясов IANA, которая действовала, когда пользователь экономил время. Таким образом, вы всегда сможете преобразовать местное время в UTC. Однако такой подход не всегда позволяет вам конвертировать UTC в правильное местное время.

Использование datetime модуля

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

  1. calendar выводит календари и предоставляет функции, используя идеализированный григорианский календарь .
  2. datetime предоставляет классы для манипулирования датами и временем.
  3. time предоставляет функции, связанные со временем, где даты не нужны.

В этом уроке вы сосредоточитесь на использовании datetime модуля Python . Основная задача datetime заключается в упрощении доступа к атрибутам объекта, связанным с датами, временем и часовыми поясами. Поскольку эти объекты очень полезны, calendar также возвращает экземпляры классов из datetime.

time менее мощный и более сложный в использовании, чем datetime. Многие функции time возвращают особый struct_time экземпляр. Этот объект имеет именованный интерфейс кортежа для доступа к хранимым данным, что делает его похожим на экземпляр datetime. Тем не менее, он не поддерживает все функции datetime, особенно возможность выполнять арифметику со значениями времени.

datetime предоставляет три класса, которые составляют интерфейс высокого уровня, который будет использовать большинство людей:

  1. datetime.date является идеализированной датой, предполагающей, что григорианский календарь простирается бесконечно в будущее и прошлое. Этот объект хранит yearmonth и в day качестве атрибутов.
  2. datetime.time это идеализированное время, предполагающее, что в день 86,400 секунд без високосных секунд. Этот объект хранит hourminutesecondmicrosecond, и tzinfo(информацию о часовом поясе).
  3. datetime.datetime является комбинацией а date и а time. Он имеет все атрибуты обоих классов.

Создание datetime экземпляров Python

Три класса, представляющие даты и время, datetime имеют похожие инициализаторы . Они могут быть созданы путем передачи аргументов ключевых слов для каждого из атрибутов, таких как yeardate или hour. Вы можете попробовать код ниже, чтобы понять, как создается каждый объект: >>>

>>> from datetime import date, time, datetime
>>> date(year=2020, month=1, day=31)
datetime.date(2020, 1, 31)
>>> time(hour=13, minute=14, second=31)
datetime.time(13, 14, 31)
>>> datetime(year=2020, month=1, day=31, hour=13, minute=14, second=31)
datetime.datetime(2020, 1, 31, 13, 14, 31)

В этом коде вы импортируете три основных класса datetime и создаете экземпляры каждого из них, передавая аргументы конструктору. Вы можете видеть, что этот код несколько многословен, и если у вас нет нужной информации в виде целых чисел , эти методы нельзя использовать для создания datetime экземпляров.

К счастью, datetime предоставляет несколько других удобных способов создания datetime экземпляров. Эти методы не требуют использования целых чисел для указания каждого атрибута, но вместо этого позволяют использовать некоторую другую информацию:

  1. date.today() создает datetime.date экземпляр с текущей локальной датой.
  2. datetime.now() создает datetime.datetime экземпляр с текущей локальной датой и временем.
  3. datetime.combine() объединяет экземпляры datetime.date и datetime.time в один datetime.datetime экземпляр.

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

>>> from datetime import date, time, datetime
>>> today = date.today()
>>> today
datetime.date(2020, 1, 24)
>>> now = datetime.now()
>>> now
datetime.datetime(2020, 1, 24, 14, 4, 57, 10015)
>>> current_time = time(now.hour, now.minute, now.second)
>>> datetime.combine(today, current_time)
datetime.datetime(2020, 1, 24, 14, 4, 57)

В этом коде, вы используете date.today()datetime.now() и datetime.combine() для создания экземпляров datedatetime и time объектов. Каждый экземпляр хранится в отдельной переменной:

  1. today это date экземпляр, который имеет только год, месяц и день.
  2. now это datetime экземпляр, который имеет год, месяц, день, час, минуту, секунду и микросекунды.
  3. current_time это time экземпляр, в котором для часов, минут и секунд установлены те же значения, что и для now.

В последней строке вы объединяете информацию о дате с информацией today о времени, current_time чтобы создать новый datetime экземпляр.

Предупреждение: datetime также предоставляет datetime.utcnow(), который возвращает экземпляр datetime в текущий UTC. Тем не менее, документация Python рекомендует не использовать этот метод, потому что он не включает информацию о часовом поясе в результирующем экземпляре.

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

Использование строк для создания datetimeэкземпляров Python

Другой способ создания date экземпляров – использовать .fromisoformat(). Чтобы использовать этот метод, вы предоставляете строку с датой в формате ISO 8601, о которой вы узнали ранее . Например, вы можете указать строку с указанием года, месяца и даты:

2020-01-31

Эта строка представляет дату 31 января 2020 года в соответствии с форматом ISO 8601. Вы можете создать date экземпляр с помощью следующего примера: >>>

>>> from datetime import date
>>> date.fromisoformat("2020-01-31")
datetime.date(2020, 1, 31)

В этом коде вы используете date.fromisoformat() для создания date экземпляра для 31 января 2020 года. Этот метод очень полезен, потому что он основан на стандарте ISO 8601. Но что, если у вас есть строка, которая представляет дату и время, но не в формате ISO 8601?

К счастью, Python datetime предоставляет метод, вызываемый .strptime() для решения этой ситуации. Этот метод использует специальный мини-язык, чтобы сообщить Python, какие части строки связаны с datetime атрибутами.

Чтобы создать datetime из строки, используя .strptime(), вы должны сообщить Python, что представляет каждая из частей строки, используя коды форматирования из мини-языка. Вы можете попробовать этот пример, чтобы увидеть, как .strptime() работает:>>>

 1 >>> date_string = "01-31-2020 14:45:37"
 2 >>> format_string = "%m-%d-%Y %H:%M:%S"

В строке 1 вы создаете date_string дату и время 31 января 2020 года в 14:45:37. В строке 2 вы создаете format_string, который использует мини-язык, чтобы указать, как части date_string будут превращаться в datetime атрибуты.

В format_string, вы включаете несколько кодов форматирования и все тире (-), двоеточия (:) и пробелы в точности так, как они отображаются date_string. Чтобы обработать дату и время date_string, вы включаете следующие коды форматирования:

Составная частьКодЦенность
Год (как четырехзначное целое)%Y2020
Месяц (как десятичное число с нулем)%m01
Дата (в виде десятичного числа с нулем)%d31
Час (в виде десятичного числа с нулевым заполнением и 24-часовыми часами)%H14
Минута (как десятичное число с нулем)%M45
Второй (в виде десятичного числа с нулем)%S37

Полный список всех опций на мини-языке выходит за рамки данного руководства, но вы можете найти несколько хороших ссылок в Интернете, в том числе в документации по Python и на веб-сайте strftime.org .

Теперь, когда date_string и format_string определены, вы можете использовать их для создания datetime экземпляра. Вот пример того, как .strptime() работает:>>>

 3 >>> from datetime import datetime
 4 >>> datetime.strptime(date_string, format_string)
 5 datetime.datetime(2020, 1, 31, 14, 45, 37)

В этом коде вы импортируете datetime в строке 3 и используете datetime.strptime() с date_string и format_string в строке 4 . Наконец, строка 5 показывает значения атрибутов в datetime экземпляре, созданном .strptime(). Вы можете видеть, что они соответствуют значениям, показанным в таблице выше.

Примечание. Существуют более продвинутые способы создания datetime экземпляров, но они включают использование сторонних библиотек, которые должны быть установлены. Называется одна особенно аккуратная библиотека dateparser, которая позволяет вам вводить строки на естественном языке. Ввод поддерживается даже на нескольких языках:>>>

 1 >>> import dateparser
 2 >>> dateparser.parse("yesterday")
 3 datetime.datetime(2020, 3, 13, 14, 39, 1, 350918)
 4 >>> dateparser.parse("morgen")
 5 datetime.datetime(2020, 3, 15, 14, 39, 7, 314754)

В этом коде вы используете dateparser для создания двух datetime экземпляров, передавая два разных строковых представления времени. В строке 1 вы импортируете dateparser. Затем в строке 2 вы используете .parse() аргумент, "yesterday" чтобы создать datetime экземпляр за двадцать четыре часа в прошлом. На момент написания статьи это было 13 марта 2020 года, в 14:39.

В строке 3 вы используете .parse() аргумент "morgen"Морген – это немецкое слово завтрашнего дня, поэтому он dateparser создает datetime экземпляр на двадцать четыре часа в будущем. На момент написания статьи это было 15 марта в 14:39.

Начало вашего обратного отсчета PyCon

Теперь у вас достаточно информации, чтобы начать работу над часами обратного отсчета для PyCon в следующем году ! PyCon US 2021 начнется 12 мая 2021 года в Питтсбурге, штат Пенсильвания. После отмены мероприятия 2020 года многие питонисты очень рады собраться в следующем году. Это отличный способ отслеживать, как долго вам нужно ждать и одновременно повышать свои datetime навыки!

Для начала создайте файл с именем pyconcd.py и добавьте этот код:

# pyconcd.py

from datetime import datetime

PYCON_DATE = datetime(year=2021, month=5, day=12, hour=8)
countdown = PYCON_DATE - datetime.now()
print(f"Countdown to PyCon US 2021: {countdown}")

В этом коде вы импортируете datetime из datetime и определяете константу PYCON_DATE, в которой хранится дата следующего PyCon US. Вы не ожидаете, что дата PyCon изменится, поэтому вы называете переменную во всех заглавных буквах, чтобы указать, что это константа.

Далее вы вычисляете разницу между datetime.now() текущим временем и PYCON_DATE. Взятие разницы между двумя datetime экземплярами возвращает datetime.timedelta экземпляр.

timedelta экземпляры представляют изменение во времени между двумя datetime экземплярами. Дельта во имя является ссылкой на греческую букву дельта, которая используется в науке и технике, означает изменение. Позже вы узнаете больше о том, как использовать timedelta для более общих арифметических операций.

Наконец, печатная продукция по состоянию на 9 апреля 2020 года, чуть раньше 21:30, выглядит следующим образом:

Countdown to PyCon US 2021: 397 days, 10:35:32.139350

Всего 397 дней до PyCon US 2021! Этот вывод немного неуклюжий, поэтому позже вы увидите, как можно улучшить форматирование. Если вы запустите этот скрипт в другой день, вы получите другой вывод. Если вы запустите скрипт после 12 мая 2021 года в 8:00, вы получите отрицательное количество оставшегося времени!

Работа с часовыми поясами

Как вы видели ранее, сохранение часового пояса, в котором происходит дата, является важным аспектом обеспечения правильности вашего кода. Python datetime предоставляет tzinfo, который является абстрактным базовым классом, который позволяет datetime.datetime и datetime.time включает информацию о часовом поясе, включая идею перехода на летнее время.

Однако datetime не обеспечивает прямой способ взаимодействия с базой данных часовых поясов IANA. Python datetime.tzinfo документация рекомендует использование пакета третьей стороной под названием dateutil. Вы можете установить dateutil с pip:

$ python -m pip install python-dateutil

Обратите внимание, что имя пакета, который вы устанавливаете из PyPI, python-dateutil отличается от имени, которое вы используете для импорта пакета, которое просто dateutil.

Использование dateutil для добавления часовых поясов в Python datetime

Одна из причин, которая dateutil так полезна, состоит в том, что она включает интерфейс к базе данных часовых поясов IANA. Это устраняет необходимость назначения часовых поясов вашим datetime экземплярам. Попробуйте этот пример, чтобы увидеть, как установить datetime экземпляр, чтобы ваш местный часовой пояс:>>>

>>> from dateutil import tz
>>> from datetime import datetime
>>> now = datetime.now(tz=tz.tzlocal())
>>> now
datetime.datetime(2020, 1, 26, 0, 55, 3, 372824, tzinfo=tzlocal())
>>> now.tzname()
'Eastern Standard Time'

В этом примере вы импортируете tz из dateutil и datetime из datetime. Затем вы создаете datetime экземпляр, установленный на текущее время, используя .now() .

Вы также передаете tz ключевое слово .now() и устанавливаете tz равным tz.tzlocal(). In dateutiltz.tzlocal() возвращает конкретный экземпляр datetime.tzinfo. Это означает, что он может представлять всю необходимую смещение часового пояса и информацию о летнем времени, которая datetime необходима.

Вы также печатаете название часового пояса, используя .tzname(), который печатает 'Eastern Standard Time'. Это вывод для Windows, но в macOS или Linux ваш вывод может быть прочитан, 'EST' если вы находитесь в восточном часовом поясе США зимой.

Вы также можете создавать часовые пояса, которые не совпадают с часовым поясом, сообщаемым вашим компьютером. Для этого вы будете использовать tz.gettz() и передавать официальное имя IANA для часового пояса, который вас интересует. Вот пример использования tz.gettz():>>>

>>> from dateutil import tz
>>> from datetime import datetime
>>> London_tz = tz.gettz("Europe/London")
>>> now = datetime.now(tz=London_tz)
>>> now
datetime.datetime(2020, 1, 26, 6, 14, 53, 513460, tzinfo=tzfile('GB-Eire'))
>>> now.tzname()
'GMT'

В этом примере вы используете, tz.gettz() чтобы получить информацию о часовом поясе для Лондона, Великобритания и сохранить ее в London_tz. Затем вы получаете текущее время, устанавливая часовой пояс на London_tz.

В Windows это дает tzinfo атрибуту значение tzfile('GB-Eire'). В macOS или Linux tzinfo атрибут будет выглядеть примерно так tzfile('/usr/share/zoneinfo/Europe/London), но он может немного отличаться в зависимости от того, откуда dateutil берется информация о часовом поясе.

Вы также используете tzname() для печати название часового пояса, которое теперь 'GMT' означает Среднее время по Гринвичу. Эти выходные данные одинаковы для Windows, MacOS и Linux.

В предыдущем разделе вы узнали, что не следует использовать .utcnow() для создания datetime экземпляра в текущем UTC. Теперь вы знаете, как использовать dateutil.tz часовой пояс для datetime экземпляра. Вот пример, измененный по рекомендации в документации Python:>>>

>>> from dateutil import tz
>>> from datetime import datetime
>>> datetime.now(tz=tz.UTC)
datetime.datetime(2020, 3, 14, 19, 1, 20, 228415, tzinfo=tzutc())

В этом коде вы используете tz.UTC для установки часового пояса часовой пояс datetime.now() UTC. Этот метод рекомендуется более использовать, utcnow() поскольку он utcnow() возвращает наивный datetime экземпляр, тогда как продемонстрированный здесь метод возвращает осведомленный datetime экземпляр.

Затем вы должны сделать небольшой обход, чтобы узнать о наивных и осведомленных datetime случаях.

Сравнение наивных и осведомленных datetime экземпляров Python

datetime Экземпляры Python поддерживают два типа операций: наивный и осведомленный. Основное различие между ними заключается в том, что наивные экземпляры не содержат информацию о часовом поясе, тогда как осведомленные экземпляры содержат. Более формально, чтобы процитировать документацию Python:

Осведомленный объект представляет определенный момент времени, который не открыт для интерпретации. Наивный объект не содержит достаточно информации, чтобы однозначно определить свое местоположение относительно других объектов даты / времени. ( Источник )

Это важное отличие для работы с Python datetime. Известный datetime экземпляр может сравнивать себя однозначно другие осведомленные datetime случаи и всегда будет возвращать правильный интервал времени при использовании в арифметических операциях.

Наивные datetime случаи, с другой стороны, могут быть неоднозначными. Один из примеров этой двусмысленности относится к летнему времени. Области, где практикуется летнее время, переводят часы вперед на один час весной и назад на один час осенью. Обычно это происходит в 2:00 по местному времени. Весной час с 2:00 до 2:59 никогда не бывает , а осенью час с 1:00 до 1:59 происходит дважды !

Практически получается, что смещение от UTC в этих часовых поясах меняется в течение года. IANA отслеживает эти изменения и каталогизирует их в разных файлах базы данных, установленных на вашем компьютере. Использование подобной библиотеки dateutil, которая использует внутреннюю базу данных IANA, является отличным способом убедиться, что ваш код правильно обрабатывает арифметику со временем.

Примечание. В Python разница между наивными и осведомленными datetime экземплярами определяется tzinfo атрибутом. У осведомленного datetime экземпляра есть tzinfo атрибут, равный подклассу datetime.tzinfo абстрактного базового класса.

Python предоставляет одну конкретную реализацию tzinfo названной timezone. Тем не менее, timezon ограничен выражением фиксированных смещений от UTC, которые не могут изменяться в течение года, поэтому он не так полезен, когда необходимо учитывать такие изменения, как переход на летнее время.

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

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

Улучшение вашего обратного отсчета PyCon

Теперь, когда вы знаете, как добавить информацию о часовом поясе к datetime экземпляру Python , вы можете улучшить свой код обратного отсчета PyCon. Ранее вы использовали стандартный datetime конструктор для передачи года, месяца, дня и часа запуска PyCon. Вы можете обновить свой код, чтобы использовать dateutil.parser модуль, который обеспечивает более естественный интерфейс для создания datetime экземпляров:

# pyconcd.py

from dateutil import parser, tz
from datetime import datetime

PYCON_DATE = parser.parse("May 12, 2021 8:00 AM")
PYCON_DATE = PYCON_DATE.replace(tzinfo=tz.gettz("America/New_York"))
now = datetime.now(tz=tz.tzlocal())

countdown = PYCON_DATE - now
print(f"Countdown to PyCon US 2021: {countdown}")

В этом коде вы импортируете parser и tz из dateutil и datetime из datetime. Далее вы используете parser.parse() для чтения даты следующего PyCon US из строки. Это гораздо более читабельно, чем простой datetime конструктор.

parser.parse() возвращает наивный datetime экземпляр, поэтому вы используете .replace() для перехода tzinfo на America/New_York часовой пояс. PyCon US 2021 будет проходить в Питтсбурге, штат Пенсильвания, который находится в восточном часовом поясе США. Каноническое название этого часового пояса – America/New_York Нью-Йорк, самый большой город в часовом поясе.

PYCON_DATE является осведомленным datetime экземпляром с часовым поясом, установленным на восточное время США. Поскольку 12 мая – после перехода на летнее время, часовой пояс будет называться 'EDT' или 'Eastern Daylight Time'.

Затем вы создаете now для представления текущего момента времени и присваиваете ему местный часовой пояс. Наконец, вы найдете timedelta между PYCON_DATE и now и распечатать результат. Если вы находитесь в локали, которая не настраивает часы на летнее время, то вы можете увидеть количество часов, оставшихся до изменения PyCon на час.

Реализация арифметики с Python datetime

datetime Экземпляры Python поддерживают несколько типов арифметики. Как вы видели ранее, это зависит от использования timedelta экземпляров для представления временных интервалов. timedelta очень полезно, потому что он встроен в стандартную библиотеку Python. Вот пример того, как работать с timedelta:>>>

>>> from datetime import datetime, timedelta
>>> now = datetime.now()
>>> now
datetime.datetime(2020, 1, 26, 9, 37, 46, 380905)
>>> tomorrow = timedelta(days=+1)
>>> now + tomorrow
datetime.datetime(2020, 1, 27, 9, 37, 46, 380905)

В этом коде, вы создаете now, в котором хранится текущее время, и tomorrow, что это timedelta из +1 дней. Далее вы добавляете now и tomorrow производите datetime экземпляр один день в будущем. Обратите внимание, что работа с наивными datetime экземплярами, как здесь, означает, что day атрибут datetime приращений на единицу и не учитывает повторные или пропущенные интервалы времени.

timedelta экземпляры также поддерживают отрицательные значения в качестве входных данных для аргументов:>>>

>>> yesterday = timedelta(days=-1)
>>> now + yesterday
datetime.datetime(2020, 1, 25, 9, 37, 46, 380905)

В этом примере вы предоставляете -1 в качестве входных данных timedelta, поэтому при добавлении now и yesterday результат в days атрибуте уменьшается на единицу .

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

>>> delta = timedelta(days=+3, hours=-4)
>>> now + delta
datetime.datetime(2020, 1, 29, 5, 37, 46, 380905)

В этом примере вы добавляете три дня и вычитаете четыре часа, поэтому новое datetime происходит в 29 января в 5:37 утра. timedelta очень полезен в этом смысле, но он несколько ограничен, потому что он не может добавлять или вычитать интервалы, превышающие день, например, месяц или год. К счастью, dateutil предоставляет более мощную замену называется relativedelta.

Основной синтаксис relativedelta очень похож на timedelta. Вы можете предоставить ключевые аргументы, которые приводят к изменениям любого количества лет, месяцев, дней, часов, секунд или микросекунд. Вы можете воспроизвести первый timedelta пример с этим кодом:>>>

>>> from dateutil.relativedelta import relativedelta
>>> tomorrow = relativedelta(days=+1)
>>> now + tomorrow
datetime.datetime(2020, 1, 27, 9, 37, 46, 380905)

В этом примере вы используете relativedelta вместо того, timedelta чтобы найти datetime соответствующее завтра. Теперь вы можете попробовать прибавить пять лет, один месяц и три дня к now вычитанию четырех часов и тридцати минут:>>>

>>> delta = relativedelta(years=+5, months=+1, days=+3, hours=-4, minutes=-30)
>>> now + delta
datetime.datetime(2025, 3, 1, 5, 7, 46, 380905)

Обратите внимание, что в этом примере дата заканчивается 1 марта 2025 года. Это связано с тем, что добавление трех дней now будет 29 января, а добавление одного месяца – 29 февраля, который существует только в високосный год. Поскольку 2025 год не является високосным, дата переносится на следующий месяц.

Вы также можете использовать relativedelta для расчета разницы между двумя datetime экземплярами. Ранее вы использовали оператор вычитания, чтобы найти разницу между двумя datetime экземплярами Python , PYCON_DATE и now. Используя relativedelta вместо того, чтобы использовать оператор вычитания, вам нужно передать два datetime экземпляра в качестве аргументов:>>>

>>> now
datetime.datetime(2020, 1, 26, 9, 37, 46, 380905)
>>> tomorrow = datetime(2020, 1, 27, 9, 37, 46, 380905)
>>> relativedelta(now, tomorrow)
relativedelta(days=-1)

В этом примере вы создаете новый datetime экземпляр tomorrow, увеличивая days поле на единицу. Затем вы используете relativedelta и передать now и tomorrow в качестве двух аргументов. dateutil затем берет разницу между этими двумя datetime экземплярами и возвращает результат как relativedelta экземпляр. В этом случае разница составляет -1 дни, так как now происходит раньше tomorrow.

dateutil.relativedelta объекты имеют бесчисленное множество других применений. Вы можете использовать их для поиска сложной календарной информации, такой как следующий год, в который 13 октября выпадает на пятницу, или какая дата будет в последнюю пятницу текущего месяца. Вы даже можете использовать их для замены атрибутов datetime экземпляра и создания, например, datetime одной недели в будущем в 10:00. Вы можете прочитать все об этих других целях в dateutil документации .

Завершение вашего обратного отсчета PyCon

Теперь у вас есть достаточно инструментов, чтобы закончить часы обратного отсчета PyCon 2021 и обеспечить хороший интерфейс для использования. В этом разделе вы будете использовать relativedelta для вычисления времени, оставшегося до PyCon, разработки функции для печати времени, оставшегося в хорошем формате, и отображения даты PyCon для пользователя.

Использование relativedelta в вашем обратном отсчете PyCon

Сначала замените оператор простого вычитания на relativedelta. С помощью оператора вычитания ваш timedelta объект не может считать интервалы времени, превышающие день. Однако relativedelta позволяет отображать оставшиеся годы, месяцы и дни:

# pyconcd.py

from dateutil import parser, tz
from dateutil.relativedelta import relativedelta
from datetime import datetime

PYCON_DATE = parser.parse("May 12, 2021 8:00 AM")
PYCON_DATE = PYCON_DATE.replace(tzinfo=tz.gettz("America/New_York"))
now = datetime.now(tz=tz.tzlocal())

countdown = relativedelta(PYCON_DATE, now)
print(f"Countdown to PyCon US 2021: {countdown}")

Единственное изменение, которое вы внесли в этот код, – заменить строку 11 на countdown = relativedelta(PYCON_DATE, now). Вывод этого скрипта должен сообщить вам, что PyCon US 2021 произойдет примерно через год и один месяц, в зависимости от того, когда вы запустите скрипт.

Однако этот вывод не очень хорош, так как выглядит как подпись relativedelta(). Вы можете создать более симпатичный вывод, заменив строку 11 в предыдущем коде следующим кодом:

def time_amount(time_unit: str, countdown: relativedelta) -> str:
    t = getattr(countdown, time_unit)
    return f"{t} {time_unit}" if t != 0 else ""

countdown = relativedelta(PYCON_DATE, now)
time_units = ["years", "months", "days", "hours", "minutes", "seconds"]
output = (t for tu in time_units if (t := time_amount(tu, countdown)))
print("Countdown to PyCon US 2021:", ", ".join(output))

Этот код требует Python 3.8, потому что он использует новый оператор моржа . Вы можете заставить этот скрипт работать на старых версиях Python, используя традиционный forцикл вместо строки output = (t for tu in time_units if (t := time_amount(tu, countdown)))

В этом коде вы определяете time_amount(), который принимает два аргумента, единицу времени и relativedelta экземпляр, из которого должны быть получены единицы времени. Если количество времени не равно нулю, тогда time_amount() возвращается строка с количеством времени и единицей времени. В противном случае он возвращает пустую строку.

Вы используете time_amount() в понимании в строке output = (t for tu in time_units if (t := time_amount(tu, countdown))) . Эта строка создает генератор, хранящий непустые строки, возвращаемые из time_amount(). Он использует оператор моржей присвоить возвращаемое значение time_amount() для t и включает в себя t только тогда , когда она True.

Наконец, строка 18 выводит конечный результат, используя .join() генератор. Далее вы посмотрите на включение даты PyCon в вывод вашего скрипта.

Отображение даты PyCon в вашем обратном отсчете PyCon

Ранее вы узнали о создании datetime экземпляров с использованием .strptime(). Этот метод использует специальный мини-язык в Python, чтобы указать, как форматируется строка даты.

В Python datetime есть дополнительный метод, .strftime() который позволяет вам форматировать datetime экземпляр в строку. В некотором смысле, это обратная операция синтаксического анализа с использованием .strptime(). Можно различать между этими двумя методами, помня о том , что p в .strptime() означает разбор , и f в .strftime() стендах для формата .

В вашем обратном отсчете PyCon вы можете использовать .strftime() для вывода вывода, чтобы сообщить пользователю дату, когда PyCon US запустится. Помните, что вы можете найти коды форматирования, которые вы хотите использовать на strftime.org . Теперь добавьте этот код вместо строки print(“Countdown to PyCon US 2021:”, “, “.join(output)) вашего скрипта обратного отсчета PyCon:

pycon_date_str = PYCON_DATE.strftime("%A, %B %d, %Y at %H:%M %p %Z")
print(f"PyCon US 2021 will start on:", pycon_date_str)
print("Countdown to PyCon US 2021:", ", ".join(output))

В этом коде строка pycon_date_str = PYCON_DATE.strftime(“%A, %B %d, %Y at %H:%M %p %Z”) используется .strftime()для создания строки, представляющей дату начала PyCon US 2021. Выходные данные включают день недели, месяц, день, год, час, минуту, AM или PM и часовой пояс:

Wednesday, May 12, 2021 at 08:00 AM EDT

В print(f”PyCon US 2021 will start on:”, pycon_date_str) вы печатаете эту строку, чтобы пользователь мог увидеть ее с пояснительным текстом. В последней строке выводится количество времени, оставшегося до даты начала PyCon. Затем вы закончите свой сценарий, чтобы другим людям было легче его использовать.

Завершение вашего обратного отсчета PyCon

Последний шаг, который вы захотите сделать, – следовать рекомендациям Python и поместить код, который выдает результат, в main() функцию. Вы можете проверить полный, окончательный код после применения всех этих изменений:

# pyconcd.py

from dateutil import parser, tz
from dateutil.relativedelta import relativedelta
from datetime import datetime

PYCON_DATE = parser.parse("May 12, 2021 8:00 AM")
PYCON_DATE = PYCON_DATE.replace(tzinfo=tz.gettz("America/New_York"))

def time_amount(time_unit: str, countdown: relativedelta) -> str:
    t = getattr(countdown, time_unit)
    return f"{t} {time_unit}" if t != 0 else ""

def main():
    now = datetime.now(tz=tz.tzlocal())
    countdown = relativedelta(PYCON_DATE, now)
    time_units = ["years", "months", "days", "hours", "minutes", "seconds"]
    output = (t for tu in time_units if (t := time_amount(tu, countdown)))
    pycon_date_str = PYCON_DATE.strftime("%A, %B %d, %Y at %H:%M %p %Z")
    print(f"PyCon US 2021 will start on:", pycon_date_str)
    print("Countdown to PyCon US 2021:", ", ".join(output))

if __name__ == "__main__":
    main()

В этом коде вы перемещаетесь print() и код, используемый для генератора в main(). В строке 23 вы используете пункт guard, чтобы убедиться, что он main() запускается только тогда, когда этот файл выполняется как скрипт . Это позволяет другим людям импортировать ваш код и повторно использоватьPYCON_DATE , например, если они захотят.

Теперь вы можете изменять этот скрипт столько раз, сколько захотите. Единственное, что нужно сделать, это разрешить пользователю изменять часовой пояс, связанный с now передачей аргумента командной строки ..

Альтернативы Python datetime и dateutil

Python datetime и dateutil мощная комбинация библиотек, когда вы работаете с датами и временем. dateutil даже рекомендуется в документации Python. Однако есть много других библиотек, которые вы можете использовать для работы с датами и временем в Python. Некоторые из них опираются на datetime и dateutil, в то время как другие являются полностью независимыми замены:

  • Pytz предоставляет информацию о часовых поясах, аналогичнуюdateutil. Он использует интерфейс несколько иной, чем стандартныйdatetime.tzinfo, поэтому будьте внимательны с потенциальными проблемами, если вы решите его использовать.
  • Arrow обеспечивает заменуdatetime. Это навеяно тем moment.js, что, если вы приехали из веб-разработки, то это может быть более знакомый интерфейс.
  • Pendulum обеспечивает еще одну заменуdatetime. Он включает интерфейс часового пояса и улучшеннуюtimedelta реализацию.
  • Maya предоставляет интерфейс, аналогичный datetime. Он полагается на маятник для частей библиотеки разбора.
  • dateparser предоставляет интерфейс для генерации datetime экземпляров из читаемого человеком текста. Это гибкий и поддерживает много языков.

Кроме того, если вы интенсивно работаете с NumPy , Pandas или другими пакетами данных , то есть несколько вариантов, которые могут быть вам полезны:

  • NumPy предоставляет API, аналогичный встроенной datetime библиотеке Python, но версия NumPy может использоваться в массивах.
  • Pandas обеспечивает поддержку данных временных рядов в DataFrames , обычно последовательных значений событий, основанных на времени, с помощьюdatetimeмодуляNumPy.
  • cftime обеспечивает поддержку календарей, отличных от григорианского календаря, а также других единиц времени, соответствующих соглашениям о климате и прогнозировании (CF). Он используетсяxarrayпакетом для обеспечения поддержки временных рядов.

Вывод

В этом уроке вы узнали о программировании с датами и временем, и почему оно часто приводит к ошибкам и путанице. Вы также узнали о Python datetime и dateutil модулях, а также о том, как работать с часовыми поясами в вашем коде.

Теперь вы можете:

  • Хранить даты в хорошем, ориентированном на будущее формате в ваших программах
  • Создать datetime экземпляры Python с отформатированными строками
  • Добавить информацию о часовом поясе к datetime экземплярам сdateutil
  • Выполнять арифметические операции с datetime экземплярами, используя relativedelta

В конце концов, вы создали скрипт, который отсчитывает время, оставшееся до следующего PyCon US. Даты и время могут быть сложными, но с этими инструментами Python в вашем арсенале, вы готовы решать самые сложные проблемы!


Совершенствуй знания каждый день у нас в Телеграм-каналах

Вопросы, реклама — VK | Telegram