classification
Title: datetime.time.isoformat function has inconsistent behavior with timezone
Type: behavior Stage:
Components: ctypes Versions: Python 3.6, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Maksym Shalenyi (Enkidulan), belopolsky, p-ganssle
Priority: normal Keywords:

Created on 2018-08-14 18:18 by Maksym Shalenyi (Enkidulan), last changed 2018-08-20 19:35 by p-ganssle.

Messages (2)
msg323531 - (view) Author: Maksym Shalenyi (Enkidulan) (Maksym Shalenyi (Enkidulan)) Date: 2018-08-14 18:18
In some cases datetime.time.isoformat shows timezone info, but in some does not. Consider the example below.

import datetime
import pytz

t = dict(hour=12, minute=31, second=21, microsecond=213456)

# `datetime.time.isoformat` has inconsistent behavior. Some of printed has timezone, but others does not.
print(datetime.time(tzinfo=pytz.timezone('Asia/Seoul'), **t).isoformat())
print(datetime.time(tzinfo=pytz.timezone('Etc/GMT-9'), **t).isoformat())
print(datetime.time(tzinfo=pytz.timezone('Australia/Sydney'), **t).isoformat())
print(datetime.time(tzinfo=pytz.timezone('Etc/UTC'), **t).isoformat())
# output:
# 12:31:21.213456
# 12:31:21.213456+09:00
# 12:31:21.213456
# 12:31:21.213456+00:00



# `datetime.time.isoformat` is inconsistent with `datetime.datetime.isoformat`. `datetime` objects always shows tz information when tz is present.
d = dict(year=2018, month=2, day=2, **t)
print(datetime.datetime(tzinfo=pytz.timezone('Asia/Seoul'), **d).isoformat())
print(datetime.datetime(tzinfo=pytz.timezone('Etc/GMT-9'), **d).isoformat())
print(datetime.datetime(tzinfo=pytz.timezone('Australia/Sydney'), **d).isoformat())
print(datetime.datetime(tzinfo=pytz.timezone('Etc/UTC'), **d).isoformat())
# output:
# 2018-02-02T12:31:21.213456+08:28
# 2018-02-02T12:31:21.213456+09:00
# 2018-02-02T12:31:21.213456+10:05
# 2018-02-02T12:31:21.213456+00:00
msg323807 - (view) Author: Paul Ganssle (p-ganssle) * (Python committer) Date: 2018-08-20 19:35
For one thing, this is not how pytz is supposed to be used. You have fallen prey to one of the most common errors when using pytz. See my blog post: https://blog.ganssle.io/articles/2018/03/pytz-fastest-footgun.html

The issue at hand is also more about what `pytz`'s time zones do than anything to do with `datetime.time`. If you use the correct `pytz` interface, you get:

>>> pytz.timezone('Asia/Seoul').localize(time(**t))
pytz/tzinfo.py in localize(self, dt, is_dst)
    321         possible_loc_dt = set()
    322         for delta in [timedelta(days=-1), timedelta(days=1)]:
--> 323             loc_dt = dt + delta
    324             idx = max(0, bisect_right(
    325                 self._utc_transition_times, loc_dt) - 1)

TypeError: unsupported operand type(s) for +: 'datetime.time' and 'datetime.timedelta'

Though this could rightly be called a bug in `pytz`. However, even using `dateutil`, you will find that this doesn't work:

>time(**t, tzinfo=tz.gettz('Asia/Seoul')).isoformat()
'12:31:21.213456'

The reason is that attaching a `tzinfo` to `datetime.time` barely makes sense, and doesn't work if the time zone depends on the day (e.g. anything with DST), because you don't *know* what day it is, so you can't get an offset. As a result, when `isoformat` attempts to query the datetime for `utcoffset()` it gets `None`, and thus has nothing to print.

It works for some of the ones you mentioned because those ones are fixed offsets that never change, and so there *is* a valid value for it, even if no date component is present. See:

>>> time(**t, tzinfo=tz.tzoffset("EST", -18000))
datetime.time(12, 31, 21, 213456, tzinfo=tzoffset('EST', -18000))

>>> time(**t, tzinfo=tz.tzoffset("EST", -18000)).isoformat()
'12:31:21.213456-05:00'

I believe this issue can be closed.
History
Date User Action Args
2018-08-20 19:35:53p-gansslesetnosy: + p-ganssle
messages: + msg323807
2018-08-17 22:30:09terry.reedysetnosy: + belopolsky

versions: - Python 3.4, Python 3.5
2018-08-14 18:18:04Maksym Shalenyi (Enkidulan)create