我知道有人问过,但那里没有提供解决方案。 Python UTC datetime object's ISO format doesn't include Z (Zulu or Zero offset)
我正在寻找一种在Python中使用此格式生成UTC时间戳的 clean 方式。我需要的格式是
2013-10-29T09:38:41.341Z
。
具体来说,我需要包括" Z"在末尾。 Python的datetime.utcnow().isoformat()
没有附加" Z"最后。
请注意,手动附加" Z"不是我能接受的解决方案。我正在寻找一种干净的方法来做到这一点。
使用后缀Z
以ISO格式生成UTC时间戳的简洁方法是什么?
答案 0 :(得分:2)
像
这样的东西datetime.utcnow().isoformat()[:-3] + 'Z'
答案 1 :(得分:1)
您可以使用arrow
库。
Arrow
尚未涵盖,请参阅github issue。而且我认为任何python库都没有做到这一点。但现在破解它非常简单。
虽然需要与pip
一起安装:
$ pip install arrow
然后获取你的iso格式,但没有Zulu格式
import arrow
arrow.utcnow().isoformat()
#'2017-02-10T08:44:38.954159+00:00'
或者你自己制作。
arrow.utcnow().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z'
# 2017-02-11T12:34:30.483Z
答案 2 :(得分:0)
zulu = "{}Z".format(arrow.utcnow().format('YYYY-MM-DDTHH:mm:ss.SSS'))
#'2018-11-28T21:54:49.639Z'
答案 3 :(得分:0)
由于缺乏声誉,我将其添加为新答案。
Tomasz Swider 解决方案是一个不错的开始,但是如果提供的时间没有微秒,[:-3]
将会缩短秒数:
我将使用utcfromtimestamp进行演示:
In [10]: datetime.utcfromtimestamp(0.1).isoformat()[:-3] + 'Z'
Out[10]: '1970-01-01T00:00:00.100Z'
In [10]: datetime.utcfromtimestamp(0).isoformat()[:-3] + 'Z'
Out[11]: '1970-01-01T00:00Z'
我认为这是一种更清洁的解决方案,可以获取毫秒级的ISO日期和Zulu时间的“ Z”:
datetime.utcnow().isoformat(timespec='milliseconds')+ 'Z'
再次使用utcfromtimestamp进行演示:
In [25]: datetime.utcfromtimestamp(0.1).isoformat(timespec='milliseconds')+ 'Z'
Out[25]: '1970-01-01T00:00:00.100Z'
In [25]: datetime.utcfromtimestamp(0).isoformat(timespec='milliseconds')+ 'Z'
Out[25]: '1970-01-01T00:00:00.000Z'
答案 4 :(得分:0)
DjangoJSONEncoder
类 django.core.serializers.json.DjangoJSONEncoder¶
JSON 序列化程序使用 DjangoJSONEncoder 进行编码。的一个子类 JSONEncoder,它处理这些附加类型:
日期时间 ECMA-262 中定义的 YYYY-MM-DDTHH:mm:ss.sssZ 或 YYYY-MM-DDTHH:mm:ss.sss+HH:MM 形式的字符串。
def default(self, o):
# See "Date Time String Format" in the ECMA-262 specification.
if isinstance(o, datetime.datetime):
r = o.isoformat()
if o.microsecond:
r = r[:23] + r[26:]
if r.endswith('+00:00'):
r = r[:-6] + 'Z'
return r
print(f"aaa{naive_utcnow.isoformat()[:23] = }")
请注意日期 datetime
对象可能包含也可能不包含时区信息(称为 naive 和 aware 日期时间对象的区别)。
在您的示例中,datetime.utcnow()
将生成一个幼稚的对象,该对象无法与 django 代码一起正常工作。
如果您希望最后总是有一个 Z
(例如为了与其他系统的互操作性,例如客户端浏览器和节点),请查看下面的脚本,其中我解释了如何到达那里以及如何处理使用 python 处理日期时间的一些常见陷阱:
from datetime import datetime, timezone
utc = timezone.utc
naive_utcnow = datetime.utcnow()
aware_utcnow = datetime.now(utc)
# there is no timezone info for naive objects here:
print(f"{naive_utcnow.isoformat() = }")
# with "+00:00":
print(f"{aware_utcnow.isoformat() = }")
# copy & paste from django implementation:
def toECMA262_django(dt: datetime):
s = dt.isoformat()
if dt.microsecond:
s = s[:23] + s[26:]
if s.endswith('+00:00'):
s = s[:-6] + 'Z'
return s
# note: django's version won't add Z for naive objects:
print(f"{toECMA262_django(naive_utcnow) = }")
# djanto's output is perfecly compatible with javacript
# for aware datetime objects:
print(f"{toECMA262_django(aware_utcnow) = }")
# improved version to treat naive objects as utc by default
def toECMA262_v2(dt: datetime, default_tz=utc):
if not dt.tzinfo:
dt = dt.replace(tzinfo=default_tz)
s = dt.isoformat()
if dt.microsecond:
s = s[:23] + s[26:]
if s.endswith('+00:00'):
s = s[:-6] + 'Z'
return s
# now has Z too:
print(f"{toECMA262_v2(naive_utcnow) = }")
print(f"{toECMA262_v2(aware_utcnow) = }")
# now works even with the misleading utcnow():
print(f"{toECMA262_v2(datetime.utcnow()) = }")
# CAREFUL: wrong result here, there is no distinction between
# naive objects returned from now() and utcnow(), the calling
# code is responsible for knowing if naive objects are in utc or not.
print(f"{toECMA262_v2(datetime.now()) = }")
# safer version, no default assumptions made
def toECMA262_v3(dt: datetime, naive_as_tz=None):
if not dt.tzinfo and naive_as_tz:
dt = dt.replace(tzinfo=naive_as_tz)
s = dt.isoformat()
if dt.microsecond:
s = s[:23] + s[26:]
if s.endswith('+00:00'):
s = s[:-6] + 'Z'
return s
# no tz offset for naive objects, unless explicitly specified:
print(f"{toECMA262_v3(naive_utcnow) = }")
print(f"{toECMA262_v3(naive_utcnow, utc) = }")
print(f"{toECMA262_v3(aware_utcnow) = }")
# no tz offset for naive objects, unless explicitly specified:
print(f"{toECMA262_v3(datetime.utcnow()) = }")
print(f"{toECMA262_v3(datetime.utcnow(), utc) = }")
# this is not wrong anymore, but no tz offset either
print(f"{toECMA262_v3(datetime.now()) = }")
# even safer, guarantees there will be a timezone or an exception is raised
def toECMA262_v4(dt: datetime, naive_as_tz=None):
if not dt.tzinfo:
if not naive_as_tz:
raise ValueError('Aware object or naive_as_tz required')
dt = dt.replace(tzinfo=naive_as_tz)
s = dt.isoformat()
if dt.microsecond:
s = s[:23] + s[26:]
if s.endswith('+00:00'):
s = s[:-6] + 'Z'
return s
def try_print(expr):
'''little helper function to print exceptions in place'''
try:
print(f"{expr} = ", end='')
print(repr(eval(expr)))
except ValueError as exc:
print(repr(exc))
# works with naive when tz is explicitly passed, otherwise raise:
try_print("toECMA262_v4(naive_utcnow, utc)")
try_print("toECMA262_v4(naive_utcnow)") # raises
try_print("toECMA262_v4(aware_utcnow)")
try_print("toECMA262_v4(datetime.utcnow(), utc)")
try_print("toECMA262_v4(datetime.utcnow())") # raises
try_print("toECMA262_v4(datetime.now())") # raises
# Please note that if have an aware object that is not in utc,
# you will not get a string ending in Z, but the proper offset
# For example:
import dateutil.tz
tzlocal = dateutil.tz.tzlocal()
aware_now = datetime.now(tzlocal)
print(f"{toECMA262_v4(aware_now) = }")
# output '2021-05-25T04:15:44.848-03:00'
# version that always output Z ended strings:
def toECMA262_v5(dt: datetime, naive_as_tz=None):
if not dt.tzinfo:
if not naive_as_tz:
raise ValueError('Aware object or naive_as_tz required')
dt = dt.replace(tzinfo=naive_as_tz)
dt = dt.astimezone(utc)
s = dt.isoformat()
if dt.microsecond:
s = s[:23] + s[26:]
if s.endswith('+00:00'):
s = s[:-6] + 'Z'
return s
# all possible cases supported and correct now, all returned with Z:
try_print("toECMA262_v5(naive_utcnow, utc)")
try_print("toECMA262_v5(naive_utcnow)") # raises
try_print("toECMA262_v5(aware_utcnow)")
try_print("toECMA262_v5(aware_now)")
try_print("toECMA262_v5(datetime.utcnow(), utc)")
try_print("toECMA262_v5(datetime.utcnow())") # raises
try_print("toECMA262_v5(datetime.now())") # raises
try_print("toECMA262_v5(datetime.now(), tzlocal)") # works fine now ;)
脚本的输出:
naive_utcnow.isoformat() = '2021-05-25T07:45:22.774853'
aware_utcnow.isoformat() = '2021-05-25T07:45:22.774856+00:00'
toECMA262_django(naive_utcnow) = '2021-05-25T07:45:22.774'
toECMA262_django(aware_utcnow) = '2021-05-25T07:45:22.774Z'
toECMA262_v2(naive_utcnow) = '2021-05-25T07:45:22.774Z'
toECMA262_v2(aware_utcnow) = '2021-05-25T07:45:22.774Z'
toECMA262_v2(datetime.utcnow()) = '2021-05-25T07:45:22.774Z'
toECMA262_v2(datetime.now()) = '2021-05-25T04:45:22.774Z'
toECMA262_v3(naive_utcnow) = '2021-05-25T07:45:22.774'
toECMA262_v3(naive_utcnow, utc) = '2021-05-25T07:45:22.774Z'
toECMA262_v3(aware_utcnow) = '2021-05-25T07:45:22.774Z'
toECMA262_v3(datetime.utcnow()) = '2021-05-25T07:45:22.775'
toECMA262_v3(datetime.utcnow(), utc) = '2021-05-25T07:45:22.775Z'
toECMA262_v3(datetime.now()) = '2021-05-25T04:45:22.775'
toECMA262_v4(naive_utcnow, utc) = '2021-05-25T07:45:22.774Z'
toECMA262_v4(naive_utcnow) = ValueError('Aware object or naive_as_tz required')
toECMA262_v4(aware_utcnow) = '2021-05-25T07:45:22.774Z'
toECMA262_v4(datetime.utcnow(), utc) = '2021-05-25T07:45:22.775Z'
toECMA262_v4(datetime.utcnow()) = ValueError('Aware object or naive_as_tz required')
toECMA262_v4(datetime.now()) = ValueError('Aware object or naive_as_tz required')
toECMA262_v4(aware_now) = '2021-05-25T04:45:22.788-03:00'
toECMA262_v5(naive_utcnow, utc) = '2021-05-25T07:45:22.774Z'
toECMA262_v5(naive_utcnow) = ValueError('Aware object or naive_as_tz required')
toECMA262_v5(aware_utcnow) = '2021-05-25T07:45:22.774Z'
toECMA262_v5(aware_now) = '2021-05-25T07:45:22.788Z'
toECMA262_v5(datetime.utcnow(), utc) = '2021-05-25T07:45:22.788Z'
toECMA262_v5(datetime.utcnow()) = ValueError('Aware object or naive_as_tz required')
toECMA262_v5(datetime.now()) = ValueError('Aware object or naive_as_tz required')
toECMA262_v5(datetime.now(), tzlocal) = '2021-05-25T07:45:22.788Z'
以上版本 5 始终输出 Z
结尾的 ECMA-262 兼容字符串,接受任何时区中的日期时间对象。如果传递了原始日期时间,则调用者代码必须指定对象是 utc、本地还是任何其他时区,并且它会自动转换为 utc。
PS:我使用 python >= 3.8 的较新 fstring 调试语法和 =
以更友好/简洁的方式打印输出,此外代码应该在 python >= 3.2 下运行良好