Python中的ISO时间(ISO 8601)


339

我有一个档案。在Python中,我想保留其创建时间,并将其转换为ISO时间(ISO 8601)字符串, 同时保留它是在东部时区(ET)创建的事实

如何获取文件的ctime并将其转换为表示东部时区的ISO时间字符串(并在必要时考虑夏令时)?



11
@Nick-有用的链接,但不是重复的-他在询问是否要进行其他转换。
Yarin

1
@ Joseph-我刚刚问过有关RFC 3999的问题,应该为此工作- 在Python中生成RFC 3339时间戳
Yarin

Answers:


559

ISO 8601的本地:

import datetime
datetime.datetime.now().isoformat()
>>> 2020-03-20T14:28:23.382748

UTC符合ISO 8601:

import datetime
datetime.datetime.utcnow().isoformat()
>>> 2020-03-20T01:30:08.180856

ISO 8601本地,无微秒:

import datetime
datetime.datetime.now().replace(microsecond=0).isoformat()
>>> 2020-03-20T14:30:43

具有TimeZone信息的UTC到ISO 8601(Python 3):

import datetime
datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat()
>>> 2020-03-20T01:31:12.467113+00:00

UTC到ISO 8601,具有本地时区信息,无微秒(Python 3):

import datetime
datetime.datetime.now().astimezone().replace(microsecond=0).isoformat()
>>> 2020-03-20T14:31:43+13:00

具有TimeZone信息的ISO 8601本地(Python 3):

import datetime
datetime.datetime.now().astimezone().isoformat()
>>> 2020-03-20T14:32:16.458361+13:00

注意astimezone()在utc时间使用时有一个错误。这给出了错误的结果:

datetime.datetime.utcnow().astimezone().isoformat() #Incorrect result

对于Python 2,请参阅并使用pytz


17
.isoformat()可以采用分隔符参数,(如果您想要除“ T”以外的其他值):.isoformat(' ')
ThorSummoner

5
@ThorSummoner很高兴认识!但是请注意,更改分隔符将不再符合ISO-8601标准……这没有什么意义(此外,还有更好的打印日期的方法,但这不是问题所在)。RFC
estani

4
空格是ISO-8601标准的一部分。It is permitted to omit the 'T' character by mutual agreement.
raychi

4
@raychi始终允许通过相互协议来更改标准(在许多情况下这会破坏标准,但是谁在乎是否是相互协议,对吗?)。我的2c:不要,就这样吧。
estani

14
这个答案是错误的,因为datetime.datetime.now()。isoformat()返回本地ISO 8601字符串,而没有将其标记为本地。您需要有一个表示本地时区的datetime.timezone,以获取isoformat来执行正确的操作。
山姆·哈特曼

66

这是我用来转换为XSD日期时间格式的方法:

from datetime import datetime
datetime.now().replace(microsecond=0).isoformat()
# You get your ISO string

我在寻找XSD日期时间格式(xs:dateTime)时遇到了这个问题。我需要从中删除微秒isoformat


4
什么是“ XSD”(在此情况下)?
彼得·莫滕森

1
我相信他的意思是xs:date来自XSD / XML样式表。
sventechie

1
这意味着xs:dateTime
radtek

56

ISO 8601时间表示

国际标准ISO 8601描述了日期和时间的字符串表示形式。此格式的两个简单示例是

2010-12-16 17:22:15
20101216T172215

(都代表2010年12月16日),但是该格式还允许亚秒级的解析时间并指定时区。这种格式当然不是特定于Python的,但是它对于以可移植格式存储日期和时间很有用。有关此格式的详细信息,请参见Markus Kuhn条目

我建议使用这种格式在文件中存储时间。

获取此表示形式中的当前时间的一种方法是使用Python标准库中时间模块中的strftime:

>>> from time import strftime
>>> strftime("%Y-%m-%d %H:%M:%S")
'2010-03-03 21:16:45'

您可以使用datetime类的strptime构造函数:

>>> from datetime import datetime
>>> datetime.strptime("2010-06-04 21:08:12", "%Y-%m-%d %H:%M:%S")
datetime.datetime(2010, 6, 4, 21, 8, 12)

最强大的是Egenix mxDateTime模块:

>>> from mx.DateTime.ISO import ParseDateTimeUTC
>>> from datetime import datetime
>>> x = ParseDateTimeUTC("2010-06-04 21:08:12")
>>> datetime.fromtimestamp(x)
datetime.datetime(2010, 3, 6, 21, 8, 12)

参考文献


2
它在什么意义上“更强大”?
斯特凡

日期:2018-05-25
loxaxs

UTC的组合日期和时间:2018-05-25T12:16:14+00:00
loxaxs,

2
UTC的组合日期和时间:2018-05-25T12:16:14Z
loxaxs,

UTC的组合日期和时间:20180525T121614Z
loxaxs,

49

我在文档中找到了datetime.isoformat 。它似乎可以满足您的要求:

datetime.isoformat([sep])

Return a string representing the date and time in ISO 8601 format, YYYY-MM-DDTHH:MM:SS.mmmmmm or, if microsecond is 0, YYYY-MM-DDTHH:MM:SS

If utcoffset() does not return None, a 6-character string is appended, giving the UTC offset in (signed) hours and minutes: YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM or, if microsecond is 0 YYYY-MM-DDTHH:MM:SS+HH:MM

The optional argument sep (default 'T') is a one-character separator, placed between the date and time portions of the result. For example,
>>>

>>> from datetime import tzinfo, timedelta, datetime
>>> class TZ(tzinfo):
...     def utcoffset(self, dt): return timedelta(minutes=-399)
...
>>> datetime(2002, 12, 25, tzinfo=TZ()).isoformat(' ')
'2002-12-25 00:00:00-06:39'

30

ISO 8601允许紧凑的表示形式,除了之外没有任何分隔符T,所以我喜欢使用这种单线来获得快速的时间戳字符串:

>>> datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%S.%fZ")
'20180905T140903.591680Z'

如果您不需要微秒,则只需忽略以下.%f部分:

>>> datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
'20180905T140903Z'

对于当地时间:

>>> datetime.datetime.now().strftime("%Y%m%dT%H%M%S")
'20180905T140903'

编辑:

阅读更多内容后,建议您不要使用标点符号。RFC3339建议使用这种样式,因为如果每个人都使用标点符号,则不会有多个ISO 8601字符串按标点符号分组的风险。因此,符合要求的字符串的一个衬里是:

>>> datetime.datetime.now().strftime("%Y-%m-%dT%H:%M%SZ")
'2018-09-05T14:09:03Z'

3
有没有办法将毫秒数限制为3?即,我的其他JavaScript应用将2019-02-23T04:02:04.051Znew Date().toISOString()
Miranda

19

ISO 8601时的格式不存储时区的名称,只有相应的UTC偏移被保留。

要将文件ctime转换为ISO 8601时间字符串,同时在Python中保留UTC偏移量3:

>>> import os
>>> from datetime import datetime, timezone
>>> ts = os.path.getctime(some_file)
>>> dt = datetime.fromtimestamp(ts, timezone.utc)
>>> dt.astimezone().isoformat()
'2015-11-27T00:29:06.839600-05:00'

该代码假定您的本地时区为 东部时区(ET),并且系统为给定的POSIX时间戳(ts)提供了正确的UTC偏移,即Python可以访问您系统上的历史时区数据库或该时区具有在给定日期使用相同的规则。

如果您需要便携式解决方案;使用pytz提供访问tz数据库模块

>>> import os
>>> from datetime import datetime
>>> import pytz  # pip install pytz
>>> ts = os.path.getctime(some_file)
>>> dt = datetime.fromtimestamp(ts, pytz.timezone('America/New_York'))
>>> dt.isoformat()
'2015-11-27T00:29:06.839600-05:00'

在这种情况下,结果是相同的。

如果您需要时区名称/缩写/时区ID,请分别存储。

>>> dt.astimezone().strftime('%Y-%m-%d %H:%M:%S%z (%Z)')
'2015-11-27 00:29:06-0500 (EST)'

注意:不能,:在UTC偏移量和EST时区中,缩写不是ISO 8601时间格式的一部分。它不是唯一的。

同一库的不同库/不同版本可能对同一日期/时区使用不同的时区规则。如果是将来的日期,那么规则可能还未知。换句话说,根据您使用的规则,相同的UTC时间可能对应于不同的本地时间-以ISO 8601格式保存时间可以保留UTC时间,并且本地时间与平台上使用的当前时区规则相对应。如果规则不同,则可能需要在其他平台上重新计算本地时间。


14

您需要使用os.stat来获取文件创建时间,并结合使用time.strftimetime.timezone进行格式化:

>>> import time
>>> import os
>>> t = os.stat('C:/Path/To/File.txt').st_ctime
>>> t = time.localtime(t)
>>> formatted = time.strftime('%Y-%m-%d %H:%M:%S', t)
>>> tz = str.format('{0:+06.2f}', float(time.timezone) / 3600)
>>> final = formatted + tz
>>> 
>>> final
'2008-11-24 14:46:08-02.00'

时区是否考虑了夏令时?似乎无论夏时制与否,tz都会相同。
约瑟夫·图里安

2
它将是当前有效的任何偏移量。如果DST处于活动状态,则它将具有与未启用DST时不同的偏移量。
Max Shawabkeh 2010年

4

如果我错了(不是),请纠正我,但是与UTC的偏差会随着夏时制而变化。所以你应该使用

tz = str.format('{0:+06.2f}', float(time.altzone) / 3600)

我还认为该标志应有所不同:

tz = str.format('{0:+06.2f}', -float(time.altzone) / 3600)

我可能是错的,但我不这么认为。


2

我同意Jarek的观点,并且还要注意ISO偏移分隔符是冒号,因此我认为最终答案应该是:

isodate.datetime_isoformat(datetime.datetime.now()) + str.format('{0:+06.2f}', -float(time.timezone) / 3600).replace('.', ':')

2

在estani的出色回答中添加了一个小的变化

具有TimeZone且没有微秒信息(Python 3)的ISO 8601本地:

import datetime, time

utc_offset_sec = time.altzone if time.localtime().tm_isdst else time.timezone
utc_offset = datetime.timedelta(seconds=-utc_offset_sec)
datetime.datetime.now().replace(microsecond=0, tzinfo=datetime.timezone(offset=utc_offset)).isoformat()

样本输出:

'2019-11-06T12:12:06-08:00'

测试了此输出可以同时被Javascript Date和C#解析DateTime/DateTimeOffset


1

我开发了此功能:

def iso_8601_format(dt):
    """YYYY-MM-DDThh:mm:ssTZD (1997-07-16T19:20:30-03:00)"""

    if dt is None:
        return ""

    fmt_datetime = dt.strftime('%Y-%m-%dT%H:%M:%S')
    tz = dt.utcoffset()
    if tz is None:
        fmt_timezone = "+00:00"
    else:
        fmt_timezone = str.format('{0:+06.2f}', float(tz.total_seconds() / 3600))

    return fmt_datetime + fmt_timezone

-6
import datetime, time    
def convert_enddate_to_seconds(self, ts):
    """Takes ISO 8601 format(string) and converts into epoch time."""
     dt = datetime.datetime.strptime(ts[:-7],'%Y-%m-%dT%H:%M:%S.%f')+\
                datetime.timedelta(hours=int(ts[-5:-3]),
                minutes=int(ts[-2:]))*int(ts[-6:-5]+'1')
    seconds = time.mktime(dt.timetuple()) + dt.microsecond/1000000.0
    return seconds 

>>> import datetime, time
>>> ts = '2012-09-30T15:31:50.262-08:00'
>>> dt = datetime.datetime.strptime(ts[:-7],'%Y-%m-%dT%H:%M:%S.%f')+ datetime.timedelta(hours=int(ts[-5:-3]), minutes=int(ts[-2:]))*int(ts[-6:-5]+'1')
>>> seconds = time.mktime(dt.timetuple()) + dt.microsecond/1000000.0
>>> seconds
1348990310.26
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.