pytz localize vs datetime replace
localize
just assumes that the naive datetime you pass it is "right" (except for not knowing about the timezone!) and so just sets the timezone, no other adjustments.
You can (and it's advisable...) internally work in UTC (rather than with naive datetimes) and use replace
when you need to perform I/O of datetimes in a localized way (normalize
will handle DST and the like).
Why python datetime replace timezone is returning different timezone?
As noted in the comments, you appear to have two problems. First is that the datetimepicker
is saving a date and time with a UTC timezone offset, when it should be applying a different timezone or leaving off the timezone offset entirely. Second is that pytz
is using the wrong offset.
I don't know how to fix the first problem, but I can help you with the second. You need to use pytz's localize
function. This only works when the datetime
doesn't have a timezone attached, but since you know the timezone is incorrect anyway you can delete it first.
tz = pytz.timezone('America/Chicago')
dt = tz.localize(dt.replace(tzinfo=None))
Is there every a benefit to use localize instead of astimezone in pytz when going from unaware to aware
To clarify my comment,
localize
sets the time zone for a naive datetime object. It does not change the object's attributes (date/time)astimezone
takes a given datetime object, naive or aware, and converts the date/time to the desired time zone. If the datetime object is naive, it is assumed to be local time.
EX:
from datetime import datetime
import pytz
# just localize to a certain time zone.
pytz.timezone("America/Denver").localize(datetime(2022, 5, 15))
Out[3]: datetime.datetime(2022, 5, 15, 0, 0, tzinfo=<DstTzInfo 'America/Denver' MDT-1 day, 18:00:00 DST>)
# convert to the desired time zone; note that my local time zone is UTC+2
# US/Denver is at UTC-6 on May 15th 2022, so total shift is 8 hours:
datetime(2022, 5, 15).astimezone(pytz.timezone("America/Denver"))
Out[4]: datetime.datetime(2022, 5, 14, 16, 0, tzinfo=<DstTzInfo 'America/Denver' MDT-1 day, 18:00:00 DST>)
To answer the question: no, there is no benefit. You're dealing with methods that have different purposes.
As a side note, the Python 3.9+ standard library actually offers a benefit:
from zoneinfo import ZoneInfo
%timeit pytz.timezone("America/Denver").localize(datetime(2022, 5, 15))
16.8 µs ± 30.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit datetime(2022, 5, 15).replace(tzinfo=ZoneInfo("America/Denver"))
1.19 µs ± 5.83 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
datetime.combine with timezone is different from datetime.now with timezone
datetime.now
effectively converts (localizes) your datetime with the pytz
timezone object - from the docs:
If tz is not None, it must be an instance of a tzinfo subclass, and
the current date and time are converted to tz’s time zone.
datetime.combine does not do that. It is as if you would write something like datetime(2020,1,1, tzinfo=pytz.timezone('US/Eastern'))
- effectively not adjusting the time zone to the provided date/time. See also e.g. pytz localize vs datetime replace and pytz: The Fastest Footgun in the West for more background info.
The correct way to get d2
with pytz
would be
d2 = EDT.localize(datetime.combine(d1.date(), d1.time()))
No such issues if using timezone objects from dateutil
or zoneinfo
(Python 3.9+):
from datetime import datetime
from zoneinfo import ZoneInfo
EDT = ZoneInfo('US/Eastern')
d1 = datetime.now(tz=EDT)
d2 = datetime.combine(d1.date(), d1.time(), tzinfo=EDT)
# d1
# Out[75]: datetime.datetime(2021, 4, 8, 7, 57, 18, 309209, tzinfo=zoneinfo.ZoneInfo(key='US/Eastern'))
# d2
# Out[76]: datetime.datetime(2021, 4, 8, 7, 57, 18, 309209, tzinfo=zoneinfo.ZoneInfo(key='US/Eastern'))
# d1 == d2
# Out[77]: True
Python datetime difference between .localize and tzinfo
When you create d2 = datetime(2015, 8, 1, 0, 0, 0, 0, local_tz)
, it does not handle daylight saving time (DST) correctly. local_tz.localize()
does.
d1
is
>>> local_tz.localize(datetime(2015, 8, 1, 0, 0, 0, 0))
datetime.datetime(
2015, 8, 1, 0, 0,
tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>
)
d2
is
>>> datetime(2015, 8, 1, 0, 0, 0, 0, local_tz)
datetime.datetime(
2015, 8, 1, 0, 0,
tzinfo=<DstTzInfo 'America/Los_Angeles' LMT-1 day, 16:07:00 STD>
)
You can see that they are not representing the same time.
d2
way is fine if you are going to work with UTC. UTC does not have daylight saving time (DST) transitions to deal with.
The correct way to handle timezone is to use local_tz.localize()
to support daylight saving time (DST)
More information and additional examples can be found here:
http://pytz.sourceforge.net/#localized-times-and-date-arithmetic
Datetime Timezone conversion using pytz
I assume you have these questions:
- why does the first function work for UTC timezone?
- why does it fail for
'US/Eastern'
timezone (DstTzInfo
instance)? - why does the second function work for all provided examples?
The first function is incorrect because it uses d.replace(tzinfo=dsttzinfo_instance)
instead of dsttzinfo_instance.localize(d)
.
The second function is correct most of the time except during ambiguous or non-existing times e.g., during DST transitions -- you can change the behaviour by passing is_dst
parameter to .localize()
: False
(default)/True
/None
(raise an exception).
The first function works for UTC timezone because it has a fixed utc offset (zero) for any date. Other timezones such as America/New_York
may have different utc offsets at different times (Daylight saving time, war time, any time that some local politician might think is a good idea -- it can be anything -- the tz database works in most cases). To implement tzinfo.utcoffset(dt)
, tzinfo.tzname(dt)
, tzinfo.dst(dt)
methods pytz
uses a collection of DstTzInfo
instances each with a different set of (_tzname, _utcoffset, _dst)
attributes. Given dt
(date/time) and is_dst
, .localize()
method chooses an appropriate (in most cases but not always) DstTzInfo
instance from the collection. pytz.timezone('America/New_York')
returns a DstTzInfo
instance with (_tzname, _utcoffset, _dst)
attributes that correspond to some undocumented moment in time (different pytz
versions may return different values -- the current version may return tzinfo
instance that corresponds to the earliest date for which zoneinfo is available -- you don't want this value most of the time: I think the motivation behind the choice of the default value is to highlight the error (passing pytz.timezone
to datetime
constructor or .replace()
method).
To summarize: .localize()
selects appropriate utcoffset, tzname, dst values, .replace()
uses the default (inappropriate) value. UTC has only one set of utcoffset, tzname, dst therefore the default value may be used and .replace()
method works with UTC timezone. You need to pass a datetime object and is_dst
parameter to select appropriate values for other timezones such as 'America/New_York'
.
In principle, pytz
could have called localize()
method to implement utcoffset()
, tzname()
, dst()
methods even if dt.tzinfo == self
: it would make these methods O(log n) in time where n
is number of intervals with different (utcoffset, tzname, dst) values but datetime
constructor and .replace()
would work as is i.e., the explicit localize()
call would be necessary only to pass is_dst
.
adding tzinfo for naive datetime cause strange offset?
Ok, i dont know whats wrong with this, but i found solution using django make_aware:
from django.utils.timezone import make_aware
def _converttime2(dt_str, tz_str):
dt = datetime.fromisoformat(dt_str)
tz = pytz.timezone(tz_str)
dt = make_aware(dt, tz)
return dt
Why doesn't pytz localize() produce a datetime object with tzinfo matching the tz object that localized it?
They are the same time zone - "Europe/Berlin"
.
When you are printing them, the output includes the abbreviation and offset that applies at that particular point in time.
If you examine the tz data sources, you'll see:
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Europe/Berlin 0:53:28 - LMT 1893 Apr
1:00 C-Eur CE%sT 1945 May 24 2:00
1:00 SovietZone CE%sT 1946
1:00 Germany CE%sT 1980
1:00 EU CE%sT
So it would appear that when the time zone has not localized a datetime, then it just uses the first entry.
It would also appear that pytz doesn't retain the extra 28 seconds from the original local mean time deviation - but that doesn't matter unless you are working with dates in Berlin before April 1893.
Related Topics
Full Examples of Using Pyserial Package
Regular Expressions: Search in List
How to Split an Iterable in Constant-Size Chunks
How to Run an Ipython Magic from a Script (Or Timing a Python Script)
How to Determine the Length of Lists in a Pandas Dataframe Column
How to Compute Derivative Using Numpy
Combining Conda Environment.Yml with Pip Requirements.Txt
Sort Tuples Based on Second Parameter
Find the End of the Month of a Pandas Dataframe Series
How to Change Plot Background Color
Angles Between Two N-Dimensional Vectors in Python
How to Update JSON File with Python
What Is the Most Efficient Way to Get First and Last Line of a Text File
Plotting Grouped Data in Same Plot Using Pandas
Replace All Occurrences of a String in a Pandas Dataframe (Python)