Формат ISO об'єкта дати Python UTC не включає Z (зміщення зулу чи нуля)


120

Чому python 2.7 не містить символу Z (зулу або зміщення нуля) в кінці UTC строки ізоформату об'єкта datetime на відміну від 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
кардамон

Це стає ще гірше, якщо в останній частині datetime дорівнює 0, вона буде усічена ...
ntg

Відповіді:


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 - особливий випадок.)


108
як я включати 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's 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.strearch ()


Додатково: Часовий пояс, читаний для людини

Далі, можливо, вам буде цікаво відображати читану людиною інформацію 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замість порожнього?
Marcello Romani

1
це повинно бути d = datetime.datetime(2014, 12, 10, 12, 0, 0, tzinfo=datetime.timezone.utc)в рядку 2. Btw ., в RFC3339 написано, що пробіл є "нормальним" (замість "T").
MrFuppes

16

Наступні сценарії javascript та python дають однакові результати. Я думаю, що це те, що ти шукаєш.

JavaScript

new Date().toISOString()

Пітон

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'

Стрілка по суті має такі ж api, як і дата, але з часовими поясами та деякими додатковими смаками, які мають бути в головній бібліотеці.

Формат, сумісний з Javascript, може бути досягнуто:

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

Javascript Date.parseспокійно скине мікросекунди з часової позначки.


7
Це не відповідає на запитання, оскільки рядок не закінчується на "Z".
калейсін

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())

2

Я підійшов до цієї проблеми з кількома цілями:

  • генерувати рядок дат, що знають UTC, у форматі ISO 8601
  • використовувати лише функції стандартної бібліотеки Python для створення об'єктів і рядків дати
  • перевірити об'єкт і рядок дати і рядок за допомогою 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

Гаразд, об’єкт дати 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

Ви також можете прочитати цю публікацію в блозі .


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)

1

Я використовую маятник:

import pendulum


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

>>> 2019-10-30T00:11:21.818265Z

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'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.