Формат 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.