[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

datetime seems to be broken WRT timezones (even when you add them)

As best I can tell, Python has no means to make use of the system's
timezone info.  In order to make datetime "timezone aware", you need
to manually create a subclass of datetime.tzinfo, whose methods return
the correct values for the timezone you care about.  In the general
case, this is hard, but since the timezone my dates are in is always
GMT, it's no problem:

class GMT(datetime.tzinfo):
    def utcoffset(self, dt):
        return datetime.timedelta(hours=0)
    def dst(self, dt):
        return datetime.timedelta(minutes=0)
    def tzname(self, dt):
        return "GMT"

Now, you can instantiate a datetime.datetime object with the times you
want, and pass an instance of this class as the tzinfo argument to the
constructor.  Also no problem:

>>> dt = datetime.datetime(2020, 1, 31, 1, 30, 45, 987654, GMT())
>>> dt
datetime.datetime(2020, 1, 31, 1, 30, 45, 987654, tzinfo=<__main__.GMT object at 0x7f9084e2add0>)
>>> print dt
2020-01-31 01:30:45.987654+00:00

Note the tzinfo object, and the +00:00 indicating this is indeed in
GMT.  If you create a "naive" datetime object, you don't get that:

>>> xy = datetime.datetime(2020, 1, 31, 1, 30, 45, 987654)
>>> xy
datetime.datetime(2020, 1, 31, 1, 30, 45, 987654)
>>> print xy
2020-01-31 01:30:45.987654

So far, so good.  However, when you go to use this object, the time it
represents is in fact wrong.  For example:

>>> print dt.strftime("%s")

Using the date command, we can easily see that it is incorrect:

# Ask for UTC, gives the wrong time
$ date -u -d "@1580452245"
Fri Jan 31 06:30:45 UTC 2020

# Ask for local time, gives the time I specified... which should have
# been in GMT, but is in my local timezone
$ date -d "@1580452245"
Fri Jan 31 01:30:45 EST 2020

And the correct value should have been:
$ date -d "2020-01-31 1:30:45 GMT" +%s

Same results under Python2.7 or Python3.

:( :( :(

I suspect this is because the underlying implementation uses
strftime() which takes struct tm, which has no field to contain the
timezone info, and does not do the necessary conversion before handing
it off.  I'm not sure what the date command is doing differently, but
it clearly is able to get the correct answer.

Does Python have an alternative way to do the above, with correct