Почему datetime.datetime.utcnow () не содержит информацию о часовом поясе?

285
datetime.datetime.utcnow()

Почему это datetimeне имеет никакой информации о часовом поясе, учитывая, что это явно UTC datetime?

Я ожидаю, что это будет содержать tzinfo.

Виталий Бабий
источник
Как преобразовать обычное поле даты в формате iso, которое имеет тип string, в формат utc?
Нави

Ответы:

192

Это означает, что это часовой пояс наивный, поэтому вы не можете использовать его с datetime.astimezone

Вы можете дать ему часовой пояс, как это

import pytz  # 3rd party: $ pip install pytz

u = datetime.utcnow()
u = u.replace(tzinfo=pytz.utc) #NOTE: it works only with a fixed utc offset

теперь вы можете менять часовые пояса

print(u.astimezone(pytz.timezone("America/New_York")))

Чтобы узнать текущее время в данном часовом поясе, вы можете передать tzinfo datetime.now()напрямую:

#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz

print(datetime.now(pytz.timezone("America/New_York")))

Он работает для любого часового пояса, включая те, которые соблюдают переход на летнее время (DST), т. Е. Он работает для часовых поясов, которые могут иметь разные смещения utc в разное время (нефиксированное смещение utc). Не используйте tz.localize(datetime.now())- он может потерпеть неудачу во время перехода в конце летнего времени, когда местное время неоднозначно.

Джон Ла Рой
источник
216
Но нет веских причин для того, чтобы он был наивным с часовым поясом - он определен как UTC. Зачем вам нужно искать стороннюю библиотеку, чтобы она работала правильно?
Марк Рэнсом
4
Я согласен; для меня «наивные» времена совершенно бесполезны. В настоящее время в списке python обсуждается вопрос о добавлении pytz в stdlib; проблема не в лицензировании, а в том, что данные о часовых поясах обновляются так часто (чего не может быть сам Python). Кроме того, pytz не реализует интерфейс tzinfo ожидаемым образом, поэтому вы можете получить ошибки, если попытаетесь использовать некоторые из часовых поясов города astimezone. Таким образом, datetime не только не имеет собственных часовых поясов, но и единственная широко распространенная реализация tzinfo не соответствует предполагаемому стандарту.
2010 года
5
@bobince Почему у вас не работают pytz и стандартные библиотеки datetime? Ядро Python и pytz, развивающиеся как независимые проекты, уменьшают логистическую сложность для основной команды. Да, снижение сложности для основной команды Python увеличивает сложность для всех пользователей Python, которым необходимо иметь дело с часовыми поясами, но я верю, что они приняли это решение по уважительной причине. Правило «Стандартная библиотека не имеет экземпляров tzinfo ...» прекрасно, потому что просто, зачем делать исключение здесь?
Дерек Лиц
15
Как насчет простоu=datetime.now(pytz.utc)
Крейг МакКуин
4
@bain: не использовать tz.localize(datetime.now()); используйте datetime.now(tz)вместо этого.
Jfs
142

Обратите внимание, что для Python 3.2 и далее, datetimeмодуль содержит datetime.timezone. Документация для datetime.utcnow()говорит:

Точную дату и время UTC можно узнать по телефону .datetime.now(timezone.utc)

Так что вы можете сделать:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2014, 7, 10, 2, 43, 55, 230107, tzinfo=datetime.timezone.utc)
Крейг МакКуин
источник
2
Что предпочтительнее? datetime.now(timezone.utc)или datetime.utcnow(timezone.utc)?
Джесси Уэбб
8
datetime.utcnow()не принимает аргументов Так и должно быть datetime.now(timezone.utc).
Крейг МакКуин
1
datetime.now()вернет время машины, но datetime.utcnow()вернет фактическое время UTC.
Бабу
13
@Babu: datetime.utcnow()не tzinfoуказывает, что это UTC. Но datetime.now(datetime.timezone.utc)возвращает время UTC с tzinfo установленным.
Крейг МакКуин
@CraigMcQueen Итак, если мы передадим tzобъект в конструктор now, он вернет время этого часового пояса? Хорошо! Спасибо за указание.
Бабу
71

Стандартные библиотеки Python не включают в себя классы tzinfo (но см. Pep 431 ). Я могу только догадываться по причинам. Лично я думаю, что было ошибкой не включать класс tzinfo для UTC, потому что он достаточно спорный, чтобы иметь стандартную реализацию.

Редактировать: Хотя в библиотеке нет реализации, в tzinfoдокументации приведен пример .

from datetime import timedelta, tzinfo

ZERO = timedelta(0)

# A UTC class.

class UTC(tzinfo):
    """UTC"""

    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO

utc = UTC()

Чтобы использовать его, чтобы получить текущее время как осведомленный объект datetime:

from datetime import datetime 

now = datetime.now(utc)

Есть datetime.timezone.utcв Python 3.2+:

from datetime import datetime, timezone 

now = datetime.now(timezone.utc)
Марк Рэнсом
источник
8
Пойди разберись, почему этот класс не был предоставлен в первую очередь (и, что более важно, использовался для datetimeобъектов, созданных utcnow()) ...
Андре Карон
17
Объект часового пояса timezone.utcбыл наконец добавлен в Python 3.2. Для обратной совместимости, utcnow()все еще возвращает объект времени без часового пояса, но вы можете получить то, что вы хотите, позвонив now(timezone.utc).
mhsmith
4
@rgove, это тот вид исправления ошибок, который должен был быть честной игрой для Python 3. Им не стоило беспокоиться об обратной совместимости. За последние несколько дней я прочитал еще один пример - structмодуль будет выполнять автоматическое преобразование из Unicode в bytestring, и окончательное решение состояло в том, чтобы нарушить совместимость с более ранними версиями Python 3, чтобы предотвратить дальнейшее неправильное решение.
Марк Рэнсом
2
Я удивлён, что в tzinfoдокументации Python есть примеры кода для его реализации, но они не включают эту функциональность в самой datetime! docs.python.org/2/library/datetime.html#datetime.tzinfo.fromutc
LS
1
@LS да, pytzэто отличный ресурс. К тому времени, как я отредактировал свой ответ, вставив пример кода, кто-то другой уже предложил его, и я не хотел красть их гром.
Марк Рэнсом
20

pytzМодуль является одним из вариантов, и есть другой python-dateutil, который , хотя и является также пакет третьей партии, уже могут быть доступны в зависимости от других зависимостей и операционной системы.

Я просто хотел включить эту методологию для справки - если вы уже установили python-dateutilдля других целей, вы можете использовать ее tzinfoвместо дублирования сpytz

import datetime
import dateutil.tz

# Get the UTC time with datetime.now:
utcdt = datetime.datetime.now(dateutil.tz.tzutc())

# Get the UTC time with datetime.utcnow:
utcdt = datetime.datetime.utcnow()
utcdt = utcdt.replace(tzinfo=dateutil.tz.tzutc())

# For fun- get the local time
localdt = datetime.datetime.now(dateutil.tz.tzlocal())

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

bbengfort
источник
1
NameError: имя 'dt' не определено
xApple
Я использовал вызов datetime.datetime.utcfromtimestamp () и мне нужно было добавить tzinfo. У меня сработало второе решение: utcdt = datetime.datetime.utcfromtimestamp(1234567890).replace(dateutil.tz.tzutc())
Ян Ли
1
примечание: в отличие от datetime.now(pytz_tz)этого всегда работает; datetime.now(dateutil.tz.tzlocal())может потерпеть неудачу во время переходов DST . PEP 495 - устранение неоднозначности по местному времени может улучшить dateutilситуацию в будущем.
JFS
@IanLee: вы можете использовать utc_dt = datetime.fromtimestamp(1234567890, dateutil.tz.tzutc())(примечание: dateutilс нефиксированным смещением utc (например, dateutil.tz.tzlocal()) здесь может произойти сбой , вместо этого используйте решение на pytzоснове ).
JFS
Поскольку моя программа уже импортирования dateutilдля dateutil.parser, мне понравилось это решение лучше. Это было так просто , как: utcCurrentTime = datetime.datetime.now(tz=dateutil.tz.tzutc()). Viola !!
LS
11

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

Действительно, Python datetime API всегда возвращает незнакомые объекты datetime, что очень печально. В самом деле, как только вы получите один из этих объектов, вы не сможете узнать, что такое часовой пояс, поэтому эти объекты сами по себе довольно «бесполезны».

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

Рекомендации:

  • Всегда используйте осведомленные datetimeобъекты, то есть с информацией о часовом поясе. Это гарантирует, что вы сможете сравнивать их напрямую ( datetime объекты со сведениями и объекты не сопоставимы) и будет правильно возвращать их пользователям. Используйте Pytz, чтобы иметь объекты часового пояса.

  • Используйте ISO 8601 в качестве формата входной и выходной строки. Используйте datetime.datetime.isoformat()для возврата меток времени в виде строки, отформатированной с использованием этого формата, который включает информацию о часовом поясе.

  • Если вам нужно проанализировать строки, содержащие метки времени в формате ISO 8601, вы можете положиться на них iso8601, которые возвращают метки времени с правильной информацией о часовом поясе. Это делает временные метки напрямую сопоставимыми.

Джо д'Андреа
источник
1
Это слегка вводящая в заблуждение рекомендация. Эмпирическое правило: никогда не работайте с часовыми поясами. Всегда сохраняйте и передавайте незанятые объекты (объекты эпохи). Часовой пояс следует рассчитывать только во время представления в пользовательском интерфейсе
17
1
Похоже, это уже вполне соответствует мысли Жюльена. Какие из его конкретных рекомендаций (как указано выше) вводят в заблуждение?
Джо д'Андреа
10

Чтобы добавить timezoneинформацию в Python 3.2+

import datetime

>>> d = datetime.datetime.now(tz=datetime.timezone.utc)
>>> print(d.tzinfo)
'UTC+00:00'
nordborn
источник
1
AttributeError: 'module' object has no attribute 'timezone' Python 2.7.13 (по умолчанию, 19 января 2017 г., 14:48:08)
Марцин Овсиани
-6
from datetime import datetime 
from dateutil.relativedelta import relativedelta
d = datetime.now()
date = datetime.isoformat(d).split('.')[0]
d_month = datetime.today() + relativedelta(months=1)
next_month = datetime.isoformat(d_month).split('.')[0]
Мрудула Атулури
источник
-13

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

Игнасио Васкес-Абрамс
источник
10
Насколько я могу судить из docs.python.org/library/datetime.html , дата- время без tzinfo - это та, где часовой пояс не указан. Здесь временная зона была задана, поэтому логически она должна присутствовать. Существует большая разница между датой / временем без ассоциированного часового пояса и датой, которая определенно указана в UTC. (В идеале это должны быть разные типы IMO, но это другое дело ...)
Джон Скит
@JonSkeet Я думаю, вы упускаете из виду Игнасио, что UTC не является часовым поясом. Удивительно, что этот ответ набрал -9 баллов, когда я набираю это ...
CS
3
@CS: Ну, Игнасио никогда не заявлял об этом ... и, хотя, строго говоря, UTC не является часовым поясом, он обычно рассматривается как один, чтобы значительно упростить жизнь (в том числе в Python, например, с помощью pytz.utc). Обратите внимание, что существует большая разница между значением, смещение которого от UTC неизвестно, и значением, для которого известно, что оно равно 0. Последнее utcnow() должно возвращать IMO. Это соответствовало бы «Знающий объект используется для представления определенного момента времени, который не открыт для интерпретации» согласно документации.
Джон Скит