ISO time (ISO 8601) in Python
Asked Answered
O

15

665

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

Oestradiol answered 27/1, 2010 at 22:22 Comment(2)
@Nick- Useful link, but not a duplicate- he's asking about converting the other way.Icarus
@Joseph- I just asked this in regards to RFC 3999- should work for this - Generate RFC 3339 timestamp in PythonIcarus
L
1148

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.now(tz=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.

Laryngoscope answered 26/1, 2015 at 9:14 Comment(16)
.isoformat() can take a separator parameter,(In case you want something other than 'T'): .isoformat(' ')Codpiece
@Codpiece good to know! But take care that changing the separator won't comply with ISO-8601 anymore... which makes little sense (besides, there are better ways to print dates but that't wasn't the question here). RFCLaryngoscope
Space is allowed as part of ISO-8601 standard. It is permitted to omit the 'T' character by mutual agreement.Afterlife
@Afterlife It's always permitted to change a standard by mutual agreement (which in many cases will break the standard, but who cares if it's mutual agreement, right?). My 2c: just don't, leave it as is.Laryngoscope
This answer is wrong because datetime.datetime.now().isoformat() returns a local ISO 8601 string without labeling it as local. You need to have a datetime.timezone representing the local timezone to get isoformat to do the right thing.Lineate
Looks like offset=utc_offset_sec should be replaced with offset=datetime.timedelta(seconds=-utc_offset_sec)Cove
The "Local to ISO-8601 with TimeZone info (python 3)" section could be improved by using: datetime.datetime.now(datetime.timezone(datetime.timedelta(seconds=time.localtime().tm_gmtoff))).isoformat(). There's no need to replace the tzinfo when you can pass it to now, and the altzone problem is resolved by using tm_gmtoff.Leahy
How to get the format of ISO-8601 in Python? Because we need to strptime the string. %Y-%m-%dT%H:%M:%S%z return -HHMM or +HHMM but not -HH:MM.Fetterlock
UTC to ISO-8601 with timezone info (uses pytz) datetime.datetime.now(tz=pytz.utc).isoformat()Cavell
but my other javascript app time is in the format 2019-02-23T04:01:55.360Z, Z for zero hour offset, isn't there an option for this format?Middy
UTC to ISO-8601 with timezone info (Python 3, without pytz): datetime.datetime.now(tz=datetime.timezone.utc).isoformat()Heartwhole
Omitting the 'T' does not change the standard since it is a part of the ISO 8601 standard.Drunkard
datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).astimezone().replace(microsecond=0).isoformat()Sandman
I went through bad times using datetime.replace(tzinfo)! I would say: avoid it! It adds minutes between the conversions. Use pytz.localize() instead. See more here.Claver
SO: how come one has to scroll down so much to find the most upvoted (and ideal) answer? Shame on you! :-DOdelle
Shorter version: datetime.now(timezone.utc).isoformat(timespec='seconds')Whore
B
121

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.now(datetime.UTC).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.now(datetime.UTC).strftime("%Y%m%dT%H%M%SZ")
'20180905T140903Z'

For local time:

>>> datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=-5))).strftime("%Y-%m-%dT%H:%M:%S%:z")
'2018-09-05T14:09:03-05:00'

In general, 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(datetime.UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
'2018-09-05T14:09:03Z'
Beef answered 5/9, 2018 at 14:10 Comment(5)
is there a way to limit the digits of milliseconds to 3? i.e. my other javascript app will produce 2019-02-23T04:02:04.051Z with new Date().toISOString()Middy
Warning: ‘Z’ at the end is incorrect. The correct way to output the time zone including the ‘Z’ symbol is ‘%z’ or ‘%Z’, which makes it ‘datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S%z")’. I’ve spent 2h trying to find a bug in my code…Chouest
@Dr_Zaszuś That's not right. %z generates a timezone offset for your local time, which is not what I recommend for timestamps, especially if you're looking at multiple systems. A literal Z is ISO 8601 shorthand for UTC.Beef
@Miranda javascript like output datetime.datetime.now(datetime.timezone.utc).isoformat()[:23]+"Z"Pyroconductivity
@Dr_Zaszuś You can also use utcnow() instead of now()Angelicaangelico
A
81

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.

Aristocrat answered 30/11, 2015 at 19:4 Comment(3)
What is "XSD" (in this context)?Psychotomimetic
I believe he means xs:date from XSD / XML stylesheets.Breadstuff
It means xs:dateTimeAristocrat
T
66

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

Turgot answered 16/12, 2010 at 16:20 Comment(6)
In what sens is it "more robust"?Stew
Date: 2018-05-25Besought
Combined date and time in UTC: 2018-05-25T12:16:14+00:00Besought
Combined date and time in UTC: 2018-05-25T12:16:14ZBesought
Combined date and time in UTC: 20180525T121614ZBesought
how to create this Combined date and time in UTC: 2018-05-25T12:16:14Z @IoxaxsCaramel
O
53

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'
Outcrop answered 28/2, 2013 at 18:19 Comment(0)
P
32

For those who are looking for a date-only solution, it is:

import datetime

datetime.date.today().isoformat()
Predatory answered 14/9, 2020 at 13:58 Comment(2)
I wound up doing it this way to avoid importing more than the datetime class: from datetime import datetime; datetime.now().isoformat()[:10] (sorry cannot find the code snippet thingy)Ommiad
Alternatively: datetime.datetime.now().strftime("%Y-%m-%d)Booby
I
26

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.

Internment answered 28/11, 2015 at 11:31 Comment(0)
B
20

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'
Benevento answered 27/1, 2010 at 23:31 Comment(2)
Does the timezone account for daylight savings time? It seems like tz will be the same regardless of daylight savings time or not.Oestradiol
It will be whatever offset is currently in effect. If DST is active, it will have a different offset from when DST is not.Benevento
H
20

Just make the life simple please:

  1. Use UTC time
  2. Microsecond
  3. one line code

f"{datetime.datetime.utcnow().isoformat()[:-3]}Z"

output:

2022-02-25T02:08:40.684Z

Hightoned answered 25/2, 2022 at 2:11 Comment(0)
U
6

Standard RFC-3339 in milliseconds

I needed the time in this format for a LoRa application so I came up with this, I hope it helps:

from datetime import datetime
from time import strftime


# Get the current time in the format: 2021-03-20T16:51:23.644+01:00
def rfc3339_time_ms():
        datetime_now = datetime.utcnow()
        # Remove the microseconds
        datetime_now_ms = datetime_now.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]
        # Add the timezone as "+/-HHMM", and the colon in "+/-HH:MM"
        datetime_now_ms_tz = datetime_now_ms + strftime("%z")
        rfc3339_ms_now = datetime_now_ms_tz[:-2] + ":" + datetime_now_ms_tz[-2:]
        # print(f"Current time in ms in RFC-3339 format: {rfc3339_ms_now}")
        return rfc3339_ms_now
Uuge answered 20/3, 2021 at 17:0 Comment(1)
This is really helpful as its mandated by Twitter API.Stowaway
L
4

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.

Lecky answered 8/6, 2010 at 8:45 Comment(0)
C
3

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

Cofferdam answered 6/11, 2019 at 17:20 Comment(0)
J
2

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('.', ':')
Jaysonjaywalk answered 27/10, 2010 at 18:58 Comment(0)
L
1

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
Lacielacing answered 8/11, 2016 at 10:4 Comment(0)
A
-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
Adiel answered 29/8, 2012 at 18:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.