Home > front end >  Why are these Python datetime conversions affected by local time (UTC offset and DST)?
Why are these Python datetime conversions affected by local time (UTC offset and DST)?

Time:07-07

I'm using Python 3.10.4 on Linux trying to convert naive datetime objects that represent UTC to a local time (UTC 1 DST).

What puzzles me is that converting a naive datetime to UTC seems to take the local timezone into consideration despite its tzinfo is None (as it should) - whereas going the other way, i.e. attempting to convert a datetime that is naive or aware (set to UTC) to the local timezone, does not seem to take DST into account.

That only works as expected when using zoneinfo (new in 3.9) instead of timezone.

import datetime

print("naive as UTC")
# naive local time UTC 1 (DST  1) 
# -> UTC 22-07-07 06:02:34
naive = datetime.datetime(2022,7,7,8,2,34)
print( naive )
utc = naive.astimezone(datetime.timezone.utc)
print(utc)


print("UTC as local")
utc = datetime.datetime(2022,7,7,8,2,34,tzinfo=datetime.timezone.utc)
print( utc )

tz = datetime.timezone( datetime.timedelta(hours=1) )
local = naive.astimezone(tz)
print(local, local.dst() )

print("UTC as local with zoneinfo")
import zoneinfo
local = utc.astimezone(zoneinfo.ZoneInfo("Europe/Berlin"))
print(local, local.dst() )

Output

naive as UTC
2022-07-07 08:02:34
2022-07-07 06:02:34 00:00
UTC as local
2022-07-07 08:02:34 00:00
2022-07-07 07:02:34 01:00 None
UTC as local with zoneinfo
2022-07-07 10:02:34 02:00 1:00:00

On the other hand, the code for "UTC as local" does seem to handle DST but not the UTC offset:

UTC as local
2022-07-07 08:02:34 00:00
2022-07-07 07:02:34 01:00 None

UTC as local
2022-01-07 08:02:34 00:00
2022-01-07 08:02:34 01:00 None

While I get expected results using zoneinfo, I'd like to understand what is happening with the other "conversions" and why the system clock affects them (running the code on https://www.python.org/shell/ gives different results).

CodePudding user response:

When you create a datetime object like this:

naive = datetime.datetime(2022,7,7,8,2,34)

You create a naive object (that is not aware of the timezone). Following the documentation:

Whether a naive object represents Coordinated Universal Time (UTC), local time, or time in some other timezone is purely up to the program, just like it is up to the program whether a particular number represents metres, miles, or mass.

I think you expect that this object will represent UTC time, but it's not true. It's simply unaware of the timezone so the timezone could be interpreted anyhow.

Let's look at source code of the astimezone method:

def astimezone(self, tz=None):

    ....

    mytz = self.tzinfo
    if mytz is None:
        mytz = self._local_timezone()
        myoffset = mytz.utcoffset(self)
    else:
       
    ....

    # Convert from UTC to tz's local time.
    return tz.fromutc(utc)

You see that when self.tzinfo is None then it interprets this as a local timezone rather than UTC

The other examples that you provided are aware of the timezone.

CodePudding user response:

To make a naive datetime object aware, do not use .astimezone(), as that assumes your system's local time for your naive object:

If self is naive, it is presumed to represent time in the system timezone.

Use .replace(tzinfo=...) instead.

  • Related