Difference between datetime.combine() and pytz.localize() - python

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.)

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.

Related

Calculating timedeltas across daylight saving

I'm facing a python timezones problem and am unsure of what is the right approach to deal with it. I have to calculate timedeltas from given start and end DateTime objects. It can happen that daylight saving time will change during the runtime of my events, so I have to take that into account.
So far I've learned that for this to work I need to save my start and end times as timezone aware DateTime objects rather than regular UTC DateTimes.
I've been looking into DateTime.tzinfo, pytz,and dateutil but from what I understand these are all mostly focused on localised display of UTC DateTime objects or calculating the offsets between different timezones. Other helpers I found expect the timezone as a UTC offset, so would already require me to know if a date is affected by daylight saving or not.
So, I guess my question is: Is there a way so save a DateTime as "Central Europe" and have it be aware of daytime savings when doing calculations with them? Or, if not, what would be the established way to check if two DateTime objects are within daylight saving, so I can manually adjust the result if necessary?
I'd be grateful for any pointers.
You just need to produce an aware (localised) datetime instance, then any calculation you do with it will take DST into account. Here as an example with pytz:
>>> import pytz
>>> from datetime import *
>>> berlin = pytz.timezone('Europe/Berlin')
>>> d1 = berlin.localize(datetime(2023, 3, 25, 12))
datetime.datetime(2023, 3, 25, 12, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>)
>>> d2 = berlin.localize(datetime(2023, 3, 26, 12))
datetime.datetime(2023, 3, 26, 12, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>)
>>> d2 - d1
datetime.timedelta(seconds=82800)
>>> (d2 - d1).total_seconds() / 60 / 60
23.0

How to render timestamp according to the timezone in Python

I have two datetime objects, they represent the same datetime value in different timezones. I would like to convert them to POSIX timestamp. However appearently calling datetime.timestamp() returns a value regardless of the timezone.
from datetime import datetime
import pytz
dt = datetime(2020, 7, 26, 6, 0)
utc_dt = pytz.utc.localize(dt) # datetime.datetime(2020, 7, 26, 6, 0, tzinfo=<UTC>)
bp = pytz.timezone("Europe/Budapest")
bp_dt = utc_dt.astimezone(bp) # datetime.datetime(2020, 7, 26, 8, 0, tzinfo=<DstTzInfo 'Europe/Budapest' CEST+2:00:00 DST>)
utc_dt.timestamp() # 1595743200.0
bp_dt.timestamp() # 1595743200.0
The documentation of datetime.timestamp() says the following:
For aware datetime instances, the return value is computed as:
(dt - datetime(1970, 1, 1, tzinfo=timezone.utc)).total_seconds()
Running utc_dt - bp_dt returns datetime.timedelta(0). So it seems it calculates with the UTC value of the datetime objects.
I use Python in a web stack. I want the backend to deal with the timezone handling and the client to recieve the precalculated datetime values in the user's timezone in the API responses.
What is the Pythonic way to get timezone aware timestamps?
In short, I would not recommend doing this because you can create a total mess, see my comment.
Technically, you could do it by simply replacing the tzinfo property of the datetime object with UTC. Note that I'm using dateutil.tz here so I can set the initial timezone directly (no localize()).
from datetime import datetime, timezone
from dateutil import tz
dt = datetime(2020, 7, 26, 6, 0, tzinfo=tz.gettz("Europe/Budapest"))
# dt.utcoffset()
# >>> datetime.timedelta(seconds=7200)
# POSIX timestamp that references to 1970-01-01 UTC:
ts_posix = dt.timestamp()
# timestamp that includes the UTC offset:
ts = dt.replace(tzinfo=timezone.utc).timestamp()
# ts-ts_posix
# >>> 7200.0

Apply timezone when convert ms to datetime

Is it possible to apply timezone when parsing ms to datetime?
My parsing is working but it is displaying local time instead of datetime with timezone:
timestamp = datetime.fromtimestamp(float(dt) / 1000.0,
tz=pytz.timezone("America/Sao_Paulo"))
Not sure if it is happening because timezone from my OS.
This just parses the timestamp as the tz you provide (so it assumes dt is a local time). If dt is absolute / UTC and you want to convert it to a local timezone, you need to first parse it it to a UTC datetime then move its timezone:
datetime.fromtimestamp(timestamp, pytz.utc).astimezone(pytz.timezone('America/Sao_Paulo'))
For instance using 1234567890:
>>> datetime.fromtimestamp(ts, pytz.utc)
datetime.datetime(2009, 2, 13, 23, 31, 30, tzinfo=<UTC>)
>>> datetime.fromtimestamp(ts, pytz.utc).astimezone(pytz.timezone('America/Sao_Paulo'))
datetime.datetime(2009, 2, 13, 21, 31, 30, tzinfo=<DstTzInfo 'America/Sao_Paulo' -02-1 day, 22:00:00 DST>)
Also note that for various historical reasons properly using pytz is not as simple as that (e.g.). If you're going to have to deal with timezones a lot, you may want to take a look at pendulum which tries to make timezone manipulations more reliable, and to provide a friendlier API.
Not sure what the problem is, it seems to be working as expected. My local time is 09:02 and with the Sao Paolo timezone it shows as 10:02, which seems correct.
>>> import datetime, time, pytz
>>> tz_1 = pytz.timezone("America/Sao_Paulo")
>>> tz_1
<DstTzInfo 'America/Sao_Paulo' LMT-1 day, 20:54:00 STD>
>>> now = time.time()
>>> now
1554382930.1575696
>>> datetime.datetime.fromtimestamp(now)
datetime.datetime(2019, 4, 4, 9, 2, 10, 157570)
>>> datetime.datetime.fromtimestamp(now, tz=tz_1)
datetime.datetime(2019, 4, 4, 10, 2, 10, 157570, tzinfo=<DstTzInfo 'America/Sao_Paulo' -03-1 day, 21:00:00 STD>)
Could you elaborate on which part is not displaying as you expected?

What's wrong with this python timezone conversion?

I want to convert a datetime with US/Eastern timezone to Budapest/Europe timezone this way:
import pytz
from datetime import datetime
ET = pytz.timezone('US/Eastern')
CET = pytz.timezone('Europe/Budapest')
time = datetime(2013, 04, 18, 0, 0, tzinfo=ET)
newTime = time.astimezone(CET)
This results newTime being: datetime.datetime(2013, 4, 18, 7, 0, tzinfo=<DstTzInfo 'Europe/Budapest' CEST+2:00:00 DST>), but it should be 2013,04,18,6,0 according to time.is and timeanddate.com converters. What do I do wrong ?
This is because of the Daylight Saving Time issue. The time passed to datetime is in the ET, not EDT, hence the result.
Take a look at pytz documentation, the preferred way is to use localize method, rather than passing tzinfo. You'll get the expected result if you amend your code to use the following line:
time = ET.localize(datetime(2013, 04, 18, 0, 0))
US/Eastern is an alias. Perhaps pytz treats it as fixed to EST instead of EDT? I'm not sure. But try America/New_York and see if that is better.

Python datetime not including DST when using pytz timezone

If I convert a UTC datetime to swedish format, summertime is included (CEST). However, while creating a datetime with sweden as the timezone, it gets CET instead of CEST. Why is this?
>>> # Modified for readability
>>> import pytz
>>> import datetime
>>> sweden = pytz.timezone('Europe/Stockholm')
>>>
>>> datetime.datetime(2010, 4, 20, 16, 20, tzinfo=pytz.utc).astimezone(sweden)
datetime(2010, 4, 20, 18, 20, tzinfo=<... 'Europe/Stockholm' CEST+2:00:00 DST>)
>>>
>>> datetime.datetime(2010, 4, 20, 18, 20, tzinfo=sweden)
datetime(2010, 4, 20, 18, 20, tzinfo=<... 'Europe/Stockholm' CET+1:00:00 STD>)
>>>
The sweden object specifies the CET time zone by default but contains enough information to know when CEST starts and stop.
In the first example, you create a datetime object and convert it to local time. The sweden object knows that the UTC time you passed occurs during daylight savings time and can convert it appropriately.
In the second example, the datetime constructor always interprets your input as not-daylight-savings-time and returns an appropriate object.
If datetime treated your input as wall-clock time and chose the appropriate daylight-savings setting for you, there would be an ambiguity during the time of year when clocks are set back. On a wall-clock the same hour occurs twice. Hence, datetime forces you to specify which timezone you're using when you create the datetime object.
Timezone abbreviations are not unique. For example "IST" could refer to "Irish Standard Time", "Iranian Standard Time", "Indian Standard Time" or "Isreali Standard Time". You shouldn't rely on parsing that, and instead should use zoneinfo timezones.

Categories

Resources