Формат ISO объекта Python UTC datetime не включает Z (смещение Zulu или Zero)

120

Почему python 2.7 не включает символ Z (Zulu или нулевое смещение) в конце строки isoformat объекта datetime UTC, в отличие от JavaScript?

>>> datetime.datetime.utcnow().isoformat()
'2013-10-29T09:14:03.895210'

В то время как в javascript

>>>  console.log(new Date().toISOString()); 
2013-10-29T09:38:41.341Z
Мурали Мопуру
источник
1
Значения даты и времени Python НЕ содержат информации о часовом поясе. Попробуйте pytz или Babel, если вы хотите, чтобы информация о часовом поясе сохранялась в вашей отметке времени.
tnknepp
67
datetime.datetime.utcnow().isoformat() + 'Z'
jfs
3
..и отсутствующий Z неожиданно приводит к тому, что некоторые вещи не работают, например, вызов API
cardamom
Становится еще хуже, если последняя часть datetime равна 0, она будет
обрезана

Ответы:

52

datetimeПо умолчанию объекты Python не имеют информации о часовом поясе, а без нее Python фактически нарушает спецификацию ISO 8601 ( если информация о часовом поясе не указана, предполагается, что это местное время ). Вы можете использовать пакет pytz, чтобы получить некоторые часовые пояса по умолчанию, или напрямую создать подкласс tzinfoсебя:

from datetime import datetime, tzinfo, timedelta
class simple_utc(tzinfo):
    def tzname(self,**kwargs):
        return "UTC"
    def utcoffset(self, dt):
        return timedelta(0)

Затем вы можете вручную добавить информацию о часовом поясе в utcnow():

>>> datetime.utcnow().replace(tzinfo=simple_utc()).isoformat()
'2014-05-16T22:51:53.015001+00:00'

Обратите внимание, что это ДЕЙСТВИТЕЛЬНО соответствует формату ISO 8601, который позволяет использовать либо, Zлибо +00:00в качестве суффикса для UTC. Обратите внимание, что последний на самом деле лучше соответствует стандарту в том, как в целом представлены часовые пояса (UTC - это особый случай).

Stiv
источник
109
как включить Zвместо +00:00?
leopoodle
14
ПРЕДУПРЕЖДЕНИЕ!!! pytzнарушает tzinfoпротокол Python и опасен в использовании! См. Blog.ganssle.io/articles/2018/03/pytz-fastest-footgun.html
ivan_pozdeev
@ivan_pozdeev спасибо за ссылку, очень интересно. Обратите внимание, что изменения, позволяющие dateutilработать, не были реализованы до Python 3.6 с PEP 495, так что pytzне использовать стандартный интерфейс несправедливо, потому что интерфейс изменился. До изменения единственный способ заставить это работать - это способ pytzсделать это.
Марк Рэнсом
67

Опция: isoformat()

Python datetimeне поддерживает суффиксы военных часовых поясов, такие как суффикс Z для UTC. Следующая простая замена строки делает свое дело:

In [1]: import datetime

In [2]: d = datetime.datetime(2014, 12, 10, 12, 0, 0)

In [3]: str(d).replace('+00:00', 'Z')
Out[3]: '2014-12-10 12:00:00Z'

str(d) по сути то же самое, что d.isoformat(sep=' ')

См .: Datetime, Стандартная библиотека Python.

Опция: strftime()

Или вы можете использовать strftimeдля достижения того же эффекта:

In [4]: d.strftime('%Y-%m-%d %H:%M:%SZ')
Out[4]: '2014-12-10 12:00:00Z'

Примечание. Этот параметр работает только в том случае, если вы знаете, что указанная дата находится в формате UTC.

См .: datetime.strftime ()


Дополнительно: часовой пояс, читаемый человеком

В дальнейшем вы можете быть заинтересованы в отображении информации о часовом поясе, удобочитаемой человеком, pytzс strftime %Zфлагом часового пояса:

In [5]: import pytz

In [6]: d = datetime.datetime(2014, 12, 10, 12, 0, 0, tzinfo=pytz.utc)

In [7]: d
Out[7]: datetime.datetime(2014, 12, 10, 12, 0, tzinfo=<UTC>)

In [8]: d.strftime('%Y-%m-%d %H:%M:%S %Z')
Out[8]: '2014-12-10 12:00:00 UTC'
Манав Катария
источник
1
Разве разделитель не должен быть Tвместо пробела?
Марчелло Романи
1
он должен быть d = datetime.datetime(2014, 12, 10, 12, 0, 0, tzinfo=datetime.timezone.utc)в строке 2. Между прочим , в RFC3339 говорится, что пробел - это «нормально» (вместо «Т»).
MrFuppes
16

Следующие скрипты javascript и python дают идентичные результаты. Я думаю, это то, что вы ищете.

JavaScript

new Date().toISOString()

Python

import datetime

datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'

На выходе они дают время в формате utc (zelda), отформатированное как строка ISO со значащей цифрой 3 миллисекунды и дополненной буквой Z.

2019-01-19T23:20:25.459Z
Рисер с вареньем
источник
12

В Python> = 3.2 вы можете просто использовать это:

>>> from datetime import datetime, timezone
>>> datetime.now(timezone.utc).isoformat()
'2019-03-14T07:55:36.979511+00:00'
Мишель Самиа
источник
3
Очень красивое решение. На их вопрос об этом Zвы можете .isoformat()[:-6] + 'Z'
вырезать
11

Даты Python немного неуклюжи. Используйте arrow.

> str(arrow.utcnow())
'2014-05-17T01:18:47.944126+00:00'

Arrow по сути имеет тот же api, что и datetime, но с часовыми поясами и некоторыми дополнительными тонкостями, которые должны быть в основной библиотеке.

Формат, совместимый с Javascript, может быть достигнут следующим образом:

arrow.utcnow().isoformat().replace("+00:00", "Z")
'2018-11-30T02:46:40.714281Z'

Javascript Date.parseнезаметно сбросит микросекунды с отметки времени.

U2EF1
источник
7
Это не отвечает на вопрос, так как строка не заканчивается на «Z».
kaleissin
3

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

In [1]: import datetime

In [1]: now = datetime.datetime.utcnow()

In [1]: now.strftime('%Y-%m-%dT%H:%M:%S') + now.strftime('.%f')[:4] + 'Z'
Out[3]: '2018-10-16T13:18:34.856Z'
Майкл Кокс
источник
3
pip install python-dateutil
>>> a = "2019-06-27T02:14:49.443814497Z"
>>> dateutil.parser.parse(a)
datetime.datetime(2019, 6, 27, 2, 14, 49, 443814, tzinfo=tzutc())
W Yg
источник
3

Я подошел к этой проблеме с несколькими целями:

  • сгенерировать строку даты и времени с учетом UTC в формате ISO 8601
  • использовать только функции стандартной библиотеки Python для создания объекта datetime и строки
  • проверить объект и строку datetime с помощью timezoneслужебной функции Django и dateutilпарсера
  • использовать функции JavaScript для проверки того, что строка даты и времени ISO 8601 поддерживает UTC

Этот подход не включает суффикс Z и не используется utcnow(), но он основан на рекомендациях в документации Python и проходит проверку с помощью Django и JavaScript.

Начнем с передачи объекта часового пояса UTC datetime.now()вместо использования datetime.utcnow():

from datetime import datetime, timezone

datetime.now(timezone.utc)
>>> datetime.datetime(2020, 1, 8, 6, 6, 24, 260810, tzinfo=datetime.timezone.utc)

datetime.now(timezone.utc).isoformat()
>>> '2020-01-08T06:07:04.492045+00:00'

Это выглядит хорошо, так что давайте посмотрим , что Джанго и dateutilдумает:

from django.utils.timezone import is_aware

is_aware(datetime.now(timezone.utc))
>>> True

import dateutil.parser

is_aware(dateutil.parser.parse(datetime.now(timezone.utc).isoformat()))
>>> True

Хорошо, объект datetime Python и строка ISO 8601 "осведомлены" о UTC. Теперь давайте посмотрим, что думает JavaScript о строке datetime. Из этого ответа получаем:

let date= '2020-01-08T06:07:04.492045+00:00';
const dateParsed = new Date(Date.parse(date))

document.write(dateParsed);
document.write("\n");
// Tue Jan 07 2020 22:07:04 GMT-0800 (Pacific Standard Time)

document.write(dateParsed.toISOString());
document.write("\n");
// 2020-01-08T06:07:04.492Z

document.write(dateParsed.toUTCString());
document.write("\n");
// Wed, 08 Jan 2020 06:07:04 GMT

Вы также можете прочитать этот пост в блоге .

highpost
источник
1
Бонус за соблюдение базовых функций библиотеки и продолжение более ранней публикации.
Виктор Ромео
1

Объединив все ответы выше, я получил следующую функцию:

from datetime import datetime, tzinfo, timedelta
class simple_utc(tzinfo):
    def tzname(self,**kwargs):
        return "UTC"
    def utcoffset(self, dt):
        return timedelta(0)


def getdata(yy, mm, dd, h, m, s) :
    d = datetime(yy, mm, dd, h, m, s)
    d = d.replace(tzinfo=simple_utc()).isoformat()
    d = str(d).replace('+00:00', 'Z')
    return d


print getdata(2018, 02, 03, 15, 0, 14)
EmptyData
источник
1

Я использую маятник:

import pendulum


d = pendulum.now("UTC").to_iso8601_string()
print(d)

>>> 2019-10-30T00:11:21.818265Z
user3249641
источник
0
>>> import arrow

>>> now = arrow.utcnow().format('YYYY-MM-DDTHH:mm:ss.SSS')
>>> now
'2018-11-28T21:34:59.235'
>>> zulu = "{}Z".format(now)
>>> zulu
'2018-11-28T21:34:59.235Z'

Или, чтобы получить его одним махом:

>>> zulu = "{}Z".format(arrow.utcnow().format('YYYY-MM-DDTHH:mm:ss.SSS'))
>>> zulu
'2018-11-28T21:54:49.639Z'
цифровая тень
источник