Python中的ISO时间(ISO 8601)

问题:Python中的ISO时间(ISO 8601)

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

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

I have a file. In Python, I would like to take its creation time, and convert it to an ISO time (ISO 8601) string while preserving the fact that it was created in the Eastern Time Zone (ET).

How do I take the file’s ctime and convert it to an ISO time string that indicates the Eastern Time Zone (and takes into account daylight savings time, if necessary)?


回答 0

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

Local to ISO 8601:

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

UTC to ISO 8601:

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

Local to ISO 8601 without microsecond:

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

UTC to ISO 8601 with TimeZone information (Python 3):

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

UTC to ISO 8601 with Local TimeZone information without microsecond (Python 3):

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

Local to ISO 8601 with TimeZone information (Python 3):

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

Notice there is a bug when using astimezone() on utc time. This gives an incorrect result:

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

For Python 2, see and use pytz.


回答 1

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

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

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

Here is what I use to convert to the XSD datetime format:

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

I came across this question when looking for the XSD date time format (xs:dateTime). I needed to remove the microseconds from isoformat.


回答 2

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)

参考文献

ISO 8601 Time Representation

The international standard ISO 8601 describes a string representation for dates and times. Two simple examples of this format are

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

(which both stand for the 16th of December 2010), but the format also allows for sub-second resolution times and to specify time zones. This format is of course not Python-specific, but it is good for storing dates and times in a portable format. Details about this format can be found in the Markus Kuhn entry.

I recommend use of this format to store times in files.

One way to get the current time in this representation is to use strftime from the time module in the Python standard library:

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

You can use the strptime constructor of the datetime class:

>>> 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)

The most robust is the Egenix mxDateTime module:

>>> 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)

References


回答 3

我在文档中找到了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'

I found the datetime.isoformat in the documentation. It seems to do what you want:

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'

回答 4

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'

ISO 8601 allows a compact representation with no separators except for the T, so I like to use this one-liner to get a quick timestamp string:

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

If you don’t need the microseconds, just leave out the .%f part:

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

For local time:

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

Edit:

After reading up on this some more, I recommend you leave the punctuation in. RFC 3339 recommends that style because if everyone uses punctuation, there isn’t a risk of things like multiple ISO 8601 strings being sorted in groups on their punctuation. So the one liner for a compliant string would be:

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

回答 5

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时间,并且本地时间与平台上使用的当前时区规则相对应。如果规则不同,则可能需要在其他平台上重新计算本地时间。

The ISO 8601 time format does not store a time zone name, only the corresponding UTC offset is preserved.

To convert a file ctime to an ISO 8601 time string while preserving the UTC offset in Python 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'

The code assumes that your local timezone is Eastern Time Zone (ET) and that your system provides a correct UTC offset for the given POSIX timestamp (ts), i.e., Python has access to a historical timezone database on your system or the time zone had the same rules at a given date.

If you need a portable solution; use the pytz module that provides access to the tz database:

>>> 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'

The result is the same in this case.

If you need the time zone name/abbreviation/zone id, store it separately.

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

Note: no, : in the UTC offset and EST timezone abbreviation is not part of the ISO 8601 time format. It is not unique.

Different libraries/different versions of the same library may use different time zone rules for the same date/timezone. If it is a future date then the rules might be unknown yet. In other words, the same UTC time may correspond to a different local time depending on what rules you use — saving a time in ISO 8601 format preserves UTC time and the local time that corresponds to the current time zone rules in use on your platform. You might need to recalculate the local time on a different platform if it has different rules.


回答 6

您需要使用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'

You’ll need to use os.stat to get the file creation time and a combination of time.strftime and time.timezone for formatting:

>>> 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'

回答 7

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

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

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

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

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

Correct me if I’m wrong (I’m not), but the offset from UTC changes with daylight saving time. So you should use

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

I also believe that the sign should be different:

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

I could be wrong, but I don’t think so.


回答 8

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

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

I agree with Jarek, and I furthermore note that the ISO offset separator character is a colon, so I think the final answer should be:

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

回答 9

在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

Adding a small variation to estani’s excellent answer

Local to ISO 8601 with TimeZone and no microsecond info (Python 3):

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()

Sample Output:

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

Tested that this output can be parsed by both Javascript Date and C# DateTime/DateTimeOffset


回答 10

我开发了此功能:

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

I’ve developed this function:

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

回答 11

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
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