In Python, how to print FULL ISO 8601 timestamp, including current timezone
Asked Answered
J

3

6

I need to print the FULL local date/time in ISO 8601 format, including the local timezone info, eg:

2007-04-05T12:30:00.0000-02:00

I can use datetime.isoformat() to print it, if I have the right tzinfo object - but how do I get that?

Note, I'm stuck at Python 2.5, which may reduce some availability of options.

Jive answered 4/7, 2014 at 13:8 Comment(4)
Could you please revise the title to make it clear what your question actually is - as it stands, this appears to be a duplicate of yesterday's effort.Rubefaction
I'm not sure how the title is unclear? Yesterday's question was incorrectly marked as a duplicate - the linked question to that does NOT answer my question - which is how to print the current, local, time with current, local, timezone.Jive
Because, as you actually state in the text of the question, your problem isn't printing the timestamp, it's getting the local tzinfo object. This clarification was added to the previous question only after I closed it.Rubefaction
Well, this is getting a bit meta now, but surely the question title should be asking how to solve the problem I want to solve (so as to be useful for future people searching for the same problem), rather than the technical detail of part of the solution?Jive
J
0

I've worked out my own way to do this, hopefully this will be useful to anyone else wanting to print useful timestamps in output files.

import datetime

# get current local time and utc time
localnow = datetime.datetime.now()
utcnow = datetime.datetime.utcnow()

# compute the time difference in seconds
tzd = localnow - utcnow
secs = tzd.days * 24 * 3600 + tzd.seconds

# get a positive or negative prefix
prefix = '+'
if secs < 0:
    prefix = '-'
    secs = abs(secs)

# print the local time with the difference, correctly formatted
suffix = "%s%02d:%02d" % (prefix, secs/3600, secs/60%60)
now = localnow.replace(microsecond=0)
print "%s%s" % (now.isoformat(' '), suffix)

This feels a little hacky, but seems the only reliable way to get a local time with the correct UTC offset. Better answers welcome!

Jive answered 14/7, 2014 at 15:32 Comment(1)
this answer is incorrect. the result returns utc +/-xy:59 because the miliseconds are in localnow and utcnow included. the substraction then causes this rounding issueBurgin
C
1

The python standard library does not provide tzinfo implementation. You need to subclass it. examples are provided in the datetime module.

The accepted answer provide wrong results. For example in my timezone +02, the result is +01:59. This is because microsecond replacement to 0 needs to be done on localnow and utcnow before the difference is calculated.

Here a version of mine for python 2.5:

# coding=utf-8


def isoformat_offset(dt, offset, dt_sep='T', hm_sep=True, short=True):
    """Return a string representing the date and time in ISO 8601 format,
    YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM. If microseconds is 0 .mmmmmm is omitted.
    The optional argument dt_sep (default 'T') is a one-character separator,
    placed between the date and time portions of the result.
    The optional argument hm_Sep (default True) indicates if a : separator
    should be placed between the hours and minutes portions of the time zone
    designator.
    The optional argument short (default True) defines if the minute portion of
    the time zone designator should be omitted in the case of zero minutes.

        >>> from datetime import datetime
        >>> cur = datetime(2017, 4, 26, 17, 14, 23, 123456)
        >>> off = 2 * 3600 # +02:00
        >>> isoformat_offset(cur, off)
        '2017-04-26T17:14:23.123456+02'
        >>> isoformat_offset(cur, off, ' ')
        '2017-04-26 17:14:23.123456+02'
        >>> isoformat_offset(cur, off, hm_sep=False)
        '2017-04-26T17:14:23.123456+02'
        >>> isoformat_offset(cur, off, short=False)
        '2017-04-26T17:14:23.123456+02:00'
        >>> isoformat_offset(cur, off, hm_sep=False, short=False)
        '2017-04-26T17:14:23.123456+0200'
        >>> cur = cur.replace(microsecond=0)
        >>> isoformat_offset(cur, off)
        '2017-04-26T17:14:23+02'
        >>> off = -2 * 3600 # -02:00
        >>> isoformat_offset(cur, off)
        '2017-04-26T17:14:23-02'
        >>> off = 2 * 3600 + 30 * 60 # +02:30
        >>> isoformat_offset(cur, off)
        '2017-04-26T17:14:23+02:30'
        >>> isoformat_offset(cur, off, hm_sep=False)
        '2017-04-26T17:14:23+0230'
    """
    offset_hours = offset // 3600
    offset_mins = (offset - offset_hours * 3600) // 60
    frmt = '%s%+03d'
    args = [dt.isoformat(dt_sep), offset_hours]
    if (short is True and offset_mins > 0) or (short is False and offset_mins == 0):
        if hm_sep is True:
            frmt += ':'
        frmt += '%02d'
        args.append(offset_mins)
    return frmt % tuple(args)

if __name__ == '__main__':
    import doctest
    doctest.testmod()

To get the local timezone in seconds as need for this function, use negated altzone from time module:

from datetime import datetime
import time

now = datetime.now()
offset = -time.altzone
print(isoformat_offset(now, offset))
Cullen answered 26/4, 2017 at 15:54 Comment(1)
Note that the docs for 'altzone' indicate that it should only be used when 'daylight' is nonzero. This suggests that a different variable contains the non-DST offset. Because of course it does. :/Meeks
E
1

Accepted @xorsyst answer gives me wrong results now (2018-03-21 in Europe/Warsow zone):

2018-03-21 19:02:10+00:59

(should be: 2018-03-21 19:02:10+01:00)

Answer given by @kwoldt is better but needs a proper offset argument. And his example gives bad result:

>>> print(isoformat_offset(datetime.now(), offset=-time.altzone, short=False))
2018-03-21T19:06:54.024151+02:00

(should be: 2018-03-21T19:06:54.024151+01:00)

I found a solution which works for me:

import datetime
import time

def local_datetime_isoformat():
    ts = time.time()
    local_dt = datetime.datetime.fromtimestamp(ts)
    struct_tm = time.localtime(ts)
    offset = time.altzone if struct_tm.tm_isdst else time.timezone
    local_iso = local_dt.isoformat(' ')
    if offset:
        sign = '+' if offset < 0 else '-'
        offset_hours = abs(offset) // 3600
        offset_minutes = (abs(offset) % 3600) // 60
        local_iso += '{0}{1:0<2}:{2:0<2}'.format(sign, offset_hours, offset_minutes)
    else:
        local_iso += 'Z'
    return local_iso

>>> print local_datetime_isoformat()
2018-03-21 19:04:03.631014+01:00
Exhaust answered 20/3, 2018 at 13:37 Comment(0)
J
0

I've worked out my own way to do this, hopefully this will be useful to anyone else wanting to print useful timestamps in output files.

import datetime

# get current local time and utc time
localnow = datetime.datetime.now()
utcnow = datetime.datetime.utcnow()

# compute the time difference in seconds
tzd = localnow - utcnow
secs = tzd.days * 24 * 3600 + tzd.seconds

# get a positive or negative prefix
prefix = '+'
if secs < 0:
    prefix = '-'
    secs = abs(secs)

# print the local time with the difference, correctly formatted
suffix = "%s%02d:%02d" % (prefix, secs/3600, secs/60%60)
now = localnow.replace(microsecond=0)
print "%s%s" % (now.isoformat(' '), suffix)

This feels a little hacky, but seems the only reliable way to get a local time with the correct UTC offset. Better answers welcome!

Jive answered 14/7, 2014 at 15:32 Comment(1)
this answer is incorrect. the result returns utc +/-xy:59 because the miliseconds are in localnow and utcnow included. the substraction then causes this rounding issueBurgin

© 2022 - 2024 — McMap. All rights reserved.