如何在保留时区的同时序列化时间对象?



我正在尝试将time对象序列化为ISO8601格式,同时使用 Python 3.8 保留它们的时区信息。

time.isoformat的文档指出:

time.isoformat(timespec='auto')

返回一个表示 ISO 8601 格式的时间的字符串,其中之一:

  • HH:MM:SS.ffffff,如果microsecond不是 0
  • HH:MM:SS, 如果microsecond为 0
  • HH:MM:SS.ffffff+HH:MM[:SS[.ffffff]],如果utcoffset()不返回None
  • HH:MM:SS+HH:MM[:SS[.ffffff]],如果microsecond为 0 并且utcoffset()不返回None

据我从该文档中了解到,我只需要生成一个time对象,其utcoffset()不是None,即可将时区信息作为ISO8601格式字符串的一部分获取。使用datetime.timezone.utc作为时区时,这工作正常:

>>> from datetime import time, timezone
>>> t = time(1, 2, 3, tzinfo=timezone.utc)
>>> t
datetime.time(1, 2, 3, tzinfo=datetime.timezone.utc)
>>> t.utcoffset()
datetime.timedelta(0)
>>> t.isoformat()
'01:02:03+00:00'

但是,一旦我想使用Python标准库未提供的时区,它似乎就不再起作用了。我尝试了dateutilpytz,在两种情况下都得到了相同的结果:

>>> from datetime import time
>>> from dateutil.tz import gettz
>>> t = time(1, 2, 3, tzinfo=gettz("US/Eastern"))
>>> t
datetime.time(1, 2, 3, tzinfo=tzfile('/usr/share/zoneinfo/US/Eastern'))
>>> t.utcoffset()
>>> t.isoformat()
'01:02:03'
>>> from datetime import time
>>> from pytz import timezone
>>> t = time(1, 2, 3, tzinfo=timezone("US/Eastern"))
>>> t
datetime.time(1, 2, 3, tzinfo=<DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>)
>>> t.utcoffset()
>>> t.isoformat()
'01:02:03'

我也尝试使用time.strftime(),但这也不能解决我的问题:

>>> from datetime import time
>>> from dateutil.tz import gettz
>>> t = time(1, 2, 3, tzinfo=gettz("US/Eastern"))
>>> t.strftime("%H:%M:%S%z")
'01:02:03'

为什么会这样,我该如何解决这个问题?

如果没有日期,时间不会给出明确的 UTC 偏移量。例如,在您的案例中,时区"美国/东部"在一年中部分具有夏令时,UTC 偏移量EST(UTC-5( 和EDT(UTC-4(。Python在那里有点安静,不会告诉你它,例如在警告中。

如果添加日期,strftime有效:

import datetime as dt
from dateutil.tz import gettz
t_est = dt.datetime.combine(dt.date(2020,1,1), dt.time(1, 2, 3, tzinfo=gettz("US/Eastern")))
t_est.strftime("%H:%M:%S%z")
# '01:02:03-0500'
t_edt = dt.datetime.combine(dt.date(2020,6,6), dt.time(1, 2, 3, tzinfo=gettz("US/Eastern")))
t_edt.strftime("%H:%M:%S%z")
# '01:02:03-0400'

另一方面,如果提供tzinfo作为纯 UTC 偏移量,则代码将起作用。引用文档:

t = dt.time.fromisoformat('04:23:01+04:00')
# datetime.time(4, 23, 1, tzinfo=datetime.timezone(datetime.timedelta(seconds=14400)))
t.isoformat()
# '04:23:01+04:00'

例如,如果您知道所有时间对象都引用 EST,则可以使用相应的 UTC 偏移量:

t = dt.time(1, 2, 3, tzinfo=dt.timezone(dt.timedelta(seconds=3600*-5)))
t.isoformat()
# '01:02:03-05:00'