在Python中生成RFC 3339时间戳

89

我正在尝试在Python中生成一个RFC 3339的UTC时间戳。到目前为止,我已经能够完成以下操作:

>>> d = datetime.datetime.now()
>>> print d.isoformat('T')
2011-12-18T20:46:00.392227

我的问题与设置UTC偏移量有关。
根据文档,classmethod datetime.now([tz]) 接受一个可选的tz参数,其中 tz 必须是 tzinfo 子类的实例,并且 datetime.tzinfo用于时区信息对象的抽象基类。 这就是我迷失的地方——为什么tzinfo是一个抽象类,我该如何实现它?

(注意: 在PHP中,只需使用timestamp = date(DATE_RFC3339);即可轻松实现,这就是为什么我无法理解Python的方法如此复杂的原因...)


在您提供的同一文档的更下方,它解释了如何实现它,并给出了一些示例,包括一个UTC类的完整代码(表示UTC),一个FixedOffset类(表示与UTC具有固定偏移量的时区,而非具有DST等的时区)以及其他一些类。 - ruakh
@ruakh - 谢谢,我错过了那些例子, LocalTimezone() 类解决了问题。 - Yarin
刚刚发现了一个类似的问题:Python中的ISO时间(ISO 8601)? - Yarin
7个回答

92

更新 2021

在 Python 3.2 中,timezone 被添加到 datetime 模块中,使您可以轻松地将时区分配给 UTC。

import datetime

n = datetime.datetime.now(datetime.timezone.utc)
n.isoformat() # '2021-07-13T15:28:51.818095+00:00'

之前的回答:

时区是个麻烦事,这也可能是为什么他们选择不在datetime库中包含它们的原因。

尝试使用pytz,它有你需要的tzinfo: http://pytz.sourceforge.net/

你需要先创建datetime对象,然后像下面一样应用时区,然后你的.isoformat()输出将包括所需的UTC偏移量:

d = datetime.datetime.utcnow()
d_with_timezone = d.replace(tzinfo=pytz.UTC)
d_with_timezone.isoformat()

'2017-04-13T14:34:23.111142+00:00'

或者,只需使用UTC,并在末尾添加一个“Z”(代表Zulu时区),以将“时区”标记为UTC。
d = datetime.datetime.utcnow() # <-- get time in UTC
print d.isoformat("T") + "Z"

'2017-04-13T14:34:23.111142Z'


@monkut- 谢谢- pytz类看起来像另一个可行的实现,但最终我使用了文档中包含的示例,根据ruakh的答案。 - Yarin
它没有给我返回微秒 :-/ - Zeinab Abbasimazar

45

在Python 3.3+版本中:

>>> from datetime import datetime, timezone                                
>>> local_time = datetime.now(timezone.utc).astimezone()
>>> local_time.isoformat()
'2015-01-16T16:52:58.547366+01:00'

在较老的Python版本中,如果你只需要一个代表当前时间的UTC感知日期时间对象,则可以像文档中展示的那样定义一个简单的tzinfo子类来表示UTC时区:(参考链接)

from datetime import datetime

utc_now = datetime.now(utc)
print(utc_now.isoformat('T'))
# -> 2015-05-19T20:32:12.610841+00:00

你可以使用tzlocal模块获取代表本地时区的pytz时区:
#!/usr/bin/env python
from datetime import datetime
from tzlocal import get_localzone # $ pip install tzlocal

now = datetime.now(get_localzone())
print(now.isoformat('T'))

这个功能可以在Python 2和3上都使用。


1
毫无疑问,Python 3.3+ 的最佳答案。 - markos.aivazoglou
2
@villapx:rfc 3339是ISO 8601的一个配置文件。恰好,datetime.isoformat()是时区感知日期时间的RFC 3339(这就是我使用它的原因)。 - jfs
1
@villapx:不,这是错误的。你在误导“潜在用户”。如果.utcoffset()不能反映.dst()值,那么你的datetime类就有问题了。 - jfs
1
@J.F.Sebastian 哇,你说得对。我完全被 datetime.tzinfo.dst() 文档 误导了:请注意,如果适用,DST 偏移量已经添加到由 utcoffset() 返回的 UTC 偏移量中,因此除非您有兴趣单独获取 DST 信息,否则无需查看 dst()。我没有直接看到上面写着 utcoffset() 的说明,其中写着 "请注意,这旨在是与 UTC 的总偏移量。" - villapx
1
@jrc:现在()本身可能会引起歧义(例如,在夏令时转换期间)。 - jfs
显示剩余6条评论

15

在现代(3.x)Python中,要获取RFC 3339 UTC时间,您所需的只是使用datetime和这一行代码(无需第三方模块):

在现代(3.x)Python中,要获得RFC 3339 UTC时间,您只需要使用datetime和以下单行代码(无需任何第三方模块):

import datetime
datetime.datetime.now(datetime.timezone.utc).isoformat()

结果类似于:'2019-06-13T15:29:28.972488+00:00'

这个ISO 8601字符串也兼容RFC3339。


实际上,Python datetime 与 ISO 8601 不兼容,因为它不允许以“Z”结尾的字符串。 - asu
没错。但我怎么才能得到以Z结尾的呢? - uwieuwe4
4
你可以在上述结果中使用.replace('+00:00','Z')。或者,只需使用.strftime('%Y-%m-%dT%H:%M:%SZ')而不是.isoformat() - JJC
Python 3.11 现在可以解析带有 Z 后缀的日期时间:https://docs.python.org/3/whatsnew/3.11.html#datetime - HTE

10

我曾经在RFC3339日期时间格式方面苦苦挣扎,但是我找到了一个适合的解决方案,可以实现date_string与datetime_object的双向转换。

你需要两个不同的外部模块,因为其中一个只能在一个方向上进行转换(不幸的是):

首先安装:

sudo pip install rfc3339
sudo pip install iso8601

然后包括:

import datetime     # for general datetime object handling
import rfc3339      # for date object -> date string
import iso8601      # for date string -> date object

为了不需要记住哪个模块是哪个方向,我编写了两个简单的帮助函数:

def get_date_object(date_string):
  return iso8601.parse_date(date_string)

def get_date_string(date_object):
  return rfc3339.rfc3339(date_object)
你可以在代码中轻松使用它,就像这样:

which inside your code you can easily use like this:

input_string = '1989-01-01T00:18:07-05:00'
test_date = get_date_object(input_string)
# >>> datetime.datetime(1989, 1, 1, 0, 18, 7, tzinfo=<FixedOffset '-05:00' datetime.timedelta(-1, 68400)>)

test_string = get_date_string(test_date)
# >>> '1989-01-01T00:18:07-05:00'

test_string is input_string # >>> True

喜大普奔!现在您可以轻松地(哈哈)使用您的日期字符串和可用的格式。


3
"Heureka"。去掉 'H'。 - SH7890

1

我最近开始使用的另一个有用的工具:dateutil 库,用于处理时区和日期解析。在 Stack Overflow 上被推荐,包括这个 answer


dateutil 是一个库,提供了将日期解析为 datetime 的功能,但我认为在生成具有特定格式的日期/时间字符串的上下文中并不是很有用。 - FObersteiner

1

pytz包适用于Python 2.X和3.X。它实现了tzinfo的具体子类,以及其他服务,因此您不必自己实现。

要添加UTC偏移量: import datetime import pytz

dt = datetime.datetime(2011, 12, 18, 20, 46, 00, 392227)
utc_dt = pytz.UTC.localize(dt)

现在是这样:

print utc_dt.isoformat()

会输出:
2011-12-18T20:46:00.392227+00:00

如果您只需要UTC时区,则无需使用pytz模块。很容易定义UTC tzinfo - jfs
谢谢。我会保存你的答案,以备需要标准库解决方案时使用。 UTC只是最短的演示方式。OP并没有特别要求。 pytz知道所有时区及其夏令时,我觉得这很有用。 - Eli_B
注意:我回答中的get_localzone()返回对应于本地时区的时区。 - jfs
是的,应该是“pytz 时区”,而不是“puts 时区”。 - jfs

0

你确实可以使用内置的datetime模块。正如@ruakh所提到的提及,页面中有展示如何使用该模块的示例。如果你查看tzinfo部分,会看到一个长篇的示例,展示了许多不同的用例。下面是你要找的代码,用于生成RFC 3339 UTC时间戳。

from datetime import tzinfo, timedelta, datetime
import time as _time

ZERO = timedelta(0)
STDOFFSET = timedelta(seconds=-_time.timezone)
if _time.daylight:
    DSTOFFSET = timedelta(seconds=-_time.altzone)
else:
    DSTOFFSET = STDOFFSET

DSTDIFF = DSTOFFSET - STDOFFSET


class LocalTimezone(tzinfo):

    def utcoffset(self, dt):
        if self._isdst(dt):
            return DSTOFFSET
        else:
            return STDOFFSET

    def dst(self, dt):
        if self._isdst(dt):
            return DSTDIFF
        else:
            return ZERO

    def tzname(self, dt):
        return _time.tzname[self._isdst(dt)]

    def _isdst(self, dt):
        tt = (dt.year, dt.month, dt.day,
              dt.hour, dt.minute, dt.second,
              dt.weekday(), 0, 0)
        stamp = _time.mktime(tt)
        tt = _time.localtime(stamp)
        return tt.tm_isdst > 0

Local = LocalTimezone()

d = datetime.now(Local)
print d.isoformat('T')

# which returns
# 2014-04-28T15:44:45.758506-07:00

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接