Format ISO obiektu datetime UTC w Pythonie nie zawiera Z (przesunięcie Zulu lub Zero)


120

Dlaczego Python 2.7 nie zawiera znaku Z (Zulu lub przesunięcia zerowego) na końcu ciągu izoformatowania obiektu daty UTC w przeciwieństwie do JavaScript?

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

Podczas gdy w javascript

>>>  console.log(new Date().toISOString()); 
2013-10-29T09:38:41.341Z

1
Wartości daty i godziny Pythona NIE mają informacji o strefie czasowej. Spróbuj pytz lub Babel, jeśli chcesz, aby informacje o strefie czasowej były przechowywane w Twoim znaczniku czasu.
tnknepp,

67
datetime.datetime.utcnow().isoformat() + 'Z'
jfs


3
.. a brakujące Z zaskakująco powoduje, że niektóre rzeczy nie działają, np. wywołanie API
kardamon

Jest jeszcze gorzej, jeśli ostatnia część daty i godziny jest równa 0, to ją obcię ...
ntg

Odpowiedzi:


52

datetimeObiekty Pythona nie mają domyślnie informacji o strefie czasowej, a bez niej Python faktycznie narusza specyfikację ISO 8601 ( jeśli nie podano informacji o strefie czasowej, przyjmuje się, że jest to czas lokalny ). Możesz użyć pakietu pytz, aby uzyskać domyślne strefy czasowe lub bezpośrednio podklasować tzinfosiebie:

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

Następnie możesz ręcznie dodać informacje o strefie czasowej do utcnow():

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

Zauważ, że ten jest zgodny z formatem ISO 8601, co pozwala na albo Zlub +00:00jako przyrostek do UTC. Zwróć uwagę, że ta ostatnia w rzeczywistości lepiej odpowiada standardowi, z ogólnym sposobem reprezentacji stref czasowych (UTC jest przypadkiem specjalnym).


108
jak dołączyć Zzamiast +00:00?
leopoodle

14
OSTRZEŻENIE!!! pytznarusza tzinfoprotokół Pythona i jest niebezpieczny w użyciu! Zobacz blog.ganssle.io/articles/2018/03/pytz-fastest-footgun.html
ivan_pozdeev

@ivan_pozdeev dzięki za link, bardzo interesujące. Zauważ, że zmiany pozwalające dateutilna pracę nie zostały zaimplementowane aż do Pythona 3.6 z PEP 495, więc powiedzieć, pytzże nie używał standardowego interfejsu, jest niesprawiedliwe, ponieważ interfejs się zmienił. Przed zmianą jedynym sposobem, aby to zadziałało, był sposób, w jaki pytzto się stało.
Mark Ransom

67

Opcja: isoformat()

Python datetimenie obsługuje wojskowych sufiksów stref czasowych, takich jak przyrostek „Z” dla UTC. Następująca prosta zamiana łańcucha załatwia sprawę:

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) jest zasadniczo taki sam jak d.isoformat(sep=' ')

Zobacz: Datetime, Python Standard Library

Opcja: strftime()

Lub możesz użyć, strftimeaby osiągnąć ten sam efekt:

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

Uwaga: ta opcja działa tylko wtedy, gdy wiesz, że określona data jest w czasie UTC.

Zobacz: datetime.strftime ()


Dodatkowe: strefa czasowa czytelna dla ludzi

Idąc dalej, możesz być zainteresowany wyświetlaniem czytelnych dla człowieka informacji pytzo strftime %Zstrefie czasowej wraz z flagą strefy czasowej:

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
Czy separator nie powinien być Tzamiast spacji?
Marcello Romani

1
powinien znajdować się d = datetime.datetime(2014, 12, 10, 12, 0, 0, tzinfo=datetime.timezone.utc)w linii 2. Przy okazji, w RFC3339 jest napisane, że spacja jest „ok” (zamiast „T”).
MrFuppes

16

Poniższe skrypty javascript i python dają identyczne wyniki. Myślę, że właśnie tego szukasz.

JavaScript

new Date().toISOString()

Pyton

import datetime

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

Wynik, jaki dają, to czas utc (zelda) sformatowany jako ciąg ISO z cyfrą znaczącą o wartości 3 milisekund i uzupełniony o Z.

2019-01-19T23:20:25.459Z

12

W Pythonie> = 3.2 możesz po prostu użyć tego:

>>> from datetime import datetime, timezone
>>> datetime.now(timezone.utc).isoformat()
'2019-03-14T07:55:36.979511+00:00'

3
Bardzo fajne rozwiązanie. Jeśli chodzi o ich pytanie dotyczące Z, możesz .isoformat()[:-6] + 'Z'
wyciąć

11

Czasy danych w Pythonie są trochę niezgrabne. Użyj arrow.

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

Arrow ma zasadniczo ten sam interfejs API, co datetime, ale ze strefami czasowymi i kilkoma dodatkowymi funkcjami, które powinny znajdować się w głównej bibliotece.

Format zgodny z Javascriptem można osiągnąć poprzez:

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

JavaScript Date.parsepo cichu usunie mikrosekundy ze znacznika czasu.


7
To nie odpowiada na pytanie, ponieważ ciąg nie kończy się na „Z”.
kaleissin

3

W poście jest wiele dobrych odpowiedzi, ale chciałem, aby format wyszedł dokładnie tak, jak w przypadku JavaScript. Tego właśnie używam i to działa dobrze.

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

3

Podszedłem do tego problemu z kilkoma celami:

  • wygeneruje „świadomy” ciąg daty i godziny UTC w formacie ISO 8601
  • używaj tylko funkcji biblioteki standardowej Python do tworzenia obiektów typu data i godzina
  • sprawdź poprawność obiektu i ciągu datetime za pomocą timezonefunkcji narzędzia Django i rozszerzeniadateutil parsera
  • użyj funkcji JavaScript, aby sprawdzić, czy łańcuch daty i godziny ISO 8601 jest zgodny z UTC

To podejście nie zawiera przyrostka Z i nie jest używane utcnow(), ale jest oparte na zaleceniach zawartych w dokumentacji Pythona i przechodzi testy z Django i JavaScript.

Zacznijmy od przekazania obiektu strefy czasowej UTC datetime.now()zamiast używania 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'

Że wygląda dobrze, więc zobaczmy, co Django i dateutilmyśleć:

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

OK, obiekt datetime w Pythonie i ciąg ISO 8601 są „świadome” UTC. Przyjrzyjmy się teraz, co myśli JavaScript o łańcuchu daty i godziny. Pożyczając z tej odpowiedzi otrzymujemy:

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

Możesz również przeczytać ten wpis na blogu .


1
Bonus za trzymanie się podstawowych funkcji biblioteki i kontynuowanie starszego postu.
Victor Romeo

1

Łącząc wszystkie powyższe odpowiedzi otrzymałem następującą funkcję:

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

Używam wahadła:

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'

Lub, aby zdobyć to za jednym zamachem:

>>> zulu = "{}Z".format(arrow.utcnow().format('YYYY-MM-DDTHH:mm:ss.SSS'))
>>> zulu
'2018-11-28T21:54:49.639Z'
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.