6

I am a bit puzzled by the following behavior. Suppose I use datetime.combine() to construct a timezone-aware object:

>>> date
datetime.date(2018, 10, 17)
>>> time
datetime.time(6, 0)
>>> tz
<DstTzInfo 'Europe/Berlin' LMT+0:53:00 STD>
>>> datetime.combine(date, time, tzinfo=tz)
datetime.datetime(2018, 10, 17, 6, 0, tzinfo=<DstTzInfo 'Europe/Berlin' LMT+0:53:00 STD>)

or I use pytz.localize() to do the same:

>>> tz.localize(datetime.combine(date, time))
datetime.datetime(2018, 10, 17, 6, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>)

Note how the tzinfo’s timezone name and offset have changed. I am unable to find a proper documentation for that behavior. The pytz documentation says

Unfortunately using the tzinfo argument of the standard datetime constructors “does not work” with pytz for many timezones.

So what exactly is going on here? (Somewhat related questions are here or here.)

Jens
  • 8,423
  • 9
  • 58
  • 78

1 Answers1

9

You just found out (again) that you should never directly add timezone information when creating timezone-aware datetimes. Always use tz.localize().

The problem you are seeing is because datetime.combine doesn't adjust the tzinfo object to the actual datetime. It still assumes the timezone information of the first valid date in this timezone, which was in the late 1800's and happened to be 0:53:00 off from UTC.

Erik Cederstrand
  • 9,643
  • 8
  • 39
  • 63
  • Thanks. Neither the Python documentation nor the pytz documentation states that, as far as I can see. Hence my question. I’m tempted to label this as a bug in the Python libs? – Jens Oct 10 '18 at 06:42
  • It's not totally obvious, but that's mostly because very little about timezones is obvious. Since you are passing a timezone instance that doesn't come from the stdlib, and the stdlib timezones only handle simple timezones without transitions where this is not an issue, then I think it's reasonable that you refer to the documentation of `pytz`, where the correct usage is quite clear. It starts by stating two correct ways to localize datetimes, and then proceeds to discourage what you just did :-) – Erik Cederstrand Oct 10 '18 at 07:02
  • Well, `tzinfo=…` _does_ work for some timezones, e.g. it’s safe apparently to pass `tzinfo=pytz.utc`. That too is mentioned somewhere in the `pytz` documentation, but again, I’d like to understand _why_ this happens. Your initial explanation of a “first valid date” does make sense though. – Jens Oct 10 '18 at 11:00
  • 2
    Thank you for this answer! To explain my own confusion: The `pytz` documentation says to avoid setting `tzinfo` when creating a `datetime.datetime`, but it doesn't really say anything about not using `combine()` with a localized `datetime.time` instance (it never mentions `datetime.time` at all). I could have guessed to do it this way, though I wouldn't have known if it is recommended... Seeing this question and your answer is helpful! – Neal Gokli Apr 24 '19 at 00:11