O formato ISO do objeto Python UTC datetime não inclui Z (Zulu ou deslocamento zero)


120

Por que o python 2.7 não inclui o caractere Z (Zulu ou deslocamento zero) no final da string de isoformato do objeto datetime UTC, ao contrário do JavaScript?

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

Considerando que em javascript

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

1
Os valores de data e hora do Python NÃO têm informações de fuso horário. Tente pytz ou Babel se quiser informações de fuso horário armazenadas em seu carimbo de hora.
tnknepp

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


3
..e a ​​falta de Z surpreendentemente faz com que algumas coisas não funcionem, por exemplo, chamada de API
cardamomo

Fica ainda pior, se a última parte de datetime for 0, ele irá truncá-lo ...
ntg

Respostas:


52

Os datetimeobjetos Python não têm informações de fuso horário por padrão e, sem elas, o Python realmente viola a especificação ISO 8601 ( se nenhuma informação de fuso horário for fornecida, presume-se que seja a hora local ). Você pode usar o pacote pytz para obter alguns fusos horários padrão ou criar suatzinfo própria subclasse :

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

Em seguida, você pode adicionar manualmente as informações de fuso horário a utcnow():

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

Observe que isso está de acordo com o formato ISO 8601, que permite tanto Zou +00:00como sufixo para UTC. Observe que o último na verdade está mais em conformidade com o padrão, com a forma como os fusos horários são representados em geral (UTC é um caso especial).


108
como faço para incluir em Zvez de +00:00?
leopoodle de

14
AVISO!!! pytzviola o tzinfoprotocolo do Python e é perigoso de usar! Consulte blog.ganssle.io/articles/2018/03/pytz-fastest-footgun.html
ivan_pozdeev

@ivan_pozdeev obrigado pelo link, muito interessante. Observe que as mudanças que permitem o dateutiltrabalho não foram implementadas até o Python 3.6 com PEP 495, então dizer pytzque não usar a interface padrão é injusto porque a interface mudou. Antes da mudança, a única maneira de fazer funcionar era da mesma maneira pytz.
Mark Ransom

67

Opção: isoformat()

O Python datetimenão suporta os sufixos de fuso horário militares como o sufixo 'Z' para UTC. A seguinte substituição simples de string resolve:

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) é essencialmente o mesmo que d.isoformat(sep=' ')

Veja: Datetime, Python Standard Library

Opção: strftime()

Ou você pode usar strftimepara obter o mesmo efeito:

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

Observação: esta opção funciona apenas quando você sabe que a data especificada está em UTC.

Veja: datetime.strftime ()


Adicional: fuso horário legível

Indo além, você pode estar interessado em exibir informações de fuso horário legíveis por humanos, pytzcom a strftime %Zsinalização de fuso horário:

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
O separador não deveria estar Tem branco?
Marcello Romani

1
deve estar d = datetime.datetime(2014, 12, 10, 12, 0, 0, tzinfo=datetime.timezone.utc)na linha 2. A propósito , em RFC3339 diz que um espaço é 'ok' (em vez do 'T').
MrFuppes

16

Os seguintes scripts javascript e python fornecem resultados idênticos. Acho que é o que você está procurando.

JavaScript

new Date().toISOString()

Pitão

import datetime

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

A saída que eles fornecem é a hora utc (zelda) formatada como uma string ISO com um dígito significativo de 3 milissegundos e anexado com um Z.

2019-01-19T23:20:25.459Z

12

Em Python> = 3.2, você pode simplesmente usar isto:

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

3
Solução muito boa. Para a pergunta sobre o Z, você pode cortar o deslocamento:.isoformat()[:-6] + 'Z'
Maximilian Burszley

11

Os datetimes do Python são um pouco desajeitados. Use arrow.

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

Arrow tem essencialmente a mesma API que datetime, mas com fusos horários e algumas sutilezas extras que deveriam estar na biblioteca principal.

Um formato compatível com Javascript pode ser obtido por:

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

O Javascript Date.parsedeixará silenciosamente os microssegundos do carimbo de data / hora.


7
Isso não responde à pergunta, pois a string não termina com 'Z'.
kaleissin de

3

Há muitas respostas boas no post, mas eu queria que o formato saísse exatamente como no JavaScript. É isso que estou usando e funciona bem.

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

Abordei esse problema com alguns objetivos:

  • gerar uma string UTC de data e hora "ciente" no formato ISO 8601
  • use apenas funções da biblioteca padrão Python para objeto datetime e criação de string
  • valide o objeto datetime e a string com uma timezonefunção de utilitário Django e o dateutilanalisador
  • use funções JavaScript para validar se a string de data e hora ISO 8601 reconhece UTC

Essa abordagem não inclui um sufixo Z e não usa utcnow(), mas é baseada na recomendação da documentação do Python e é aprovada com Django e JavaScript.

Vamos começar passando um objeto de fuso horário UTC para, em datetime.now()vez de usar 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'

Isso parece bom, então vamos ver o que Django e dateutilpensar:

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, o objeto datetime do Python e a string ISO 8601 são "cientes" do UTC. Agora, vamos ver o que o JavaScript pensa da string datetime. Pegando emprestado esta resposta , obtemos:

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

Você também pode querer ler esta postagem do blog .


1
Bônus por seguir as funções básicas da biblioteca e acompanhar uma postagem mais antiga.
Victor Romeo

1

Ao combinar todas as respostas acima, obtive a seguinte função:

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

Eu uso o pêndulo:

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'

Ou, para obtê-lo de uma só vez:

>>> zulu = "{}Z".format(arrow.utcnow().format('YYYY-MM-DDTHH:mm:ss.SSS'))
>>> zulu
'2018-11-28T21:54:49.639Z'
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.