Using python Im trying to localize a datetime value to timezone "America/Chicago" which is -06:00 currently.
I get the timezone the following way:
>>> import pytz
>>> tz = pytz.timezone("America/Chicago")
<DstTzInfo 'America/Chicago' CST-1 day, 18:00:00 STD>
when I localize a date:
>>> my_date = tz.localize(datetime.now())
datetime.datetime(2016, 9, 24, 17, 4, 43, 439824, tzinfo=<DstTzInfo 'America/Chicago' CDT-1 day, 19:00:00 DST>)
Notice it is the wrong timezone after localize:
<DstTzInfo 'America/Chicago' CDT-1 day, 19:00:00 DST>
And later when i ask for the offset, you can see it is confirmed it has the wrong offset:
>>> my_date.strftime("%z")
'-0500'
Exactly the same happend if I use astimezone instead:
>>>my_date
datetime.datetime(2016, 9, 24, 22, 15, 1, 620364, tzinfo=<UTC>)
>>>my_date.astimezone(tz)
datetime.datetime(2016, 9, 24, 17, 15, 1, 620364, tzinfo=<DstTzInfo 'America/Chicago' CDT-1 day, 19:00:00 DST>)
Btw Chicago is observing DST now. So -05.00 is the right offset. Pytz timezone by default has the standard time, but when localized can account for day light saving based on the date(as in your case).
Related
python version: 3.7
I am trying to convert utc timestamps to a given timezone using pytz and astimezone.
(I have timestamps stored in my db in utc timezone and I amtrynig to convert them)
For example:
converting '2022-07-18 19:43:26.164345' timestamp to 'US/Pacific' timezone will result '2022-07-18 12:43:26.164345-07:00'
converting '2022-07-18 19:43:26.164345' timestamp to 'UTC' timezone will not affect any change - will result '2022-07-18 19:43:26.164345'
here is what I tried:
import pytz
def convert_utc_timestamp_to_timezone(utc_timestamp, dt_timezone):
return utc_timestamp.astimezone(pytz.timezone(dt_timezone))
expected:
> dt = datetime.datetime.now()
> convert_utc_timestamp_to_timezone(dt, 'UTC') // expected result: dt
but no.., actual result::
> dt = datetime.datetime.now()
> converted = convert_utc_timestamp_to_timezone(dt, 'UTC')
> print(dt) # 2022-07-18 23:10:34.061169
> print(converted) # 2022-07-18 20:10:34.061169+00:00
# dt != converted not as I expect
This works when my local timezone is set to utc - for example when running on online browser
import datetime
import pytz
def convert_utc_timestamp_to_timezone(utc_timestamp, dt_timezone):
return utc_timestamp.astimezone(pytz.timezone(dt_timezone))
dt = datetime.datetime.utcnow()
a = convert_utc_timestamp_to_timezone(dt, 'US/Pacific')
print('in utc timezone',dt)
print('after convert to US/Pacific timezone: ',a)
# results:
#in utc timezone 2022-07-18 20:18:37.253062
# after convert to US/Pacific timezone: 2022-07-18 13:18:37.253062-07:00
dt2 = datetime.datetime.utcnow()
a2 = convert_utc_timestamp_to_timezone(dt, 'UTC')
print('in utc timezone', dt2)
print('after convert to UTC timezone: ',a2)
# results:
# in utc timezone 2022-07-18 20:18:37.264363
# after convert to UTC timezone: 2022-07-18 20:18:37.253062+00:00
But does not work well when running locally - since my pc timezone isn't utc
(the doc actually states that astimezone “converts to local time”)
import pytz
def convert_utc_timestamp_to_timezone(utc_timestamp, dt_timezone):
return utc_timestamp.astimezone(pytz.timezone(dt_timezone))
dt = datetime.datetime.utcnow()
a = convert_utc_timestamp_to_timezone(dt, 'US/Pacific')
print('in utc timezone', dt)
print('after convert to US/Pacific timezone: ', a)
# result:
# in utc timezone 2022-07-18 20:22:05.526588
# after convert to US/Pacific timezone: 2022-07-18 10:22:05.526588-07:00
dt2 = datetime.datetime.utcnow()
a2 = convert_utc_timestamp_to_timezone(dt, 'UTC')
print('in utc timezone', dt2)
print('after convert to UTC timezone: ', a2)
# result:
# in utc timezone 2022-07-18 20:22:05.576213
# after convert to UTC timezone: 2022-07-18 17:22:05.526588+00:00
what is the correct way to convert timestamp in utc timezone to a given timezone?
I tried pytz localize , but also doesn't give expected result
This is related to a common gotcha with Python datetime types, kinda due to legacy code reasons. The short answer is that you're mixing timezone aware and timezone un-aware types.
Here's the right way to get a utc timestamp:
In [85]: bad_utc = dt.datetime.utcnow() # no timezone info
...: (bad_utc, bad_utc.astimezone(pytz.utc))
...:
Out[85]:
(
datetime.datetime(2022, 7, 18, 20, 23, 44, 583377),
datetime.datetime(2022, 7, 19, 3, 23, 44, 583377, tzinfo=<UTC>),
)
In [86]: good_utc = dt.datetime.now(datetime.timezone.utc) # has timezone info
...: (good_utc, good_utc.astimezone(pytz.utc))
...:
Out[86]:
(
datetime.datetime(2022, 7, 18, 20, 23, 47, 323579, tzinfo=datetime.timezone.utc),
datetime.datetime(2022, 7, 18, 20, 23, 47, 323579, tzinfo=<UTC>),
)
For this reason, there are libraries like pendulum that "do the right thing" and avoid these gotchas, e.g.:
In [79]: dt.datetime.utcnow() # wrong
Out[79]: datetime.datetime(2022, 7, 18, 20, 20, 41, 681750)
In [80]: dt.datetime.now(datetime.timezone.utc) # right
Out[80]: datetime.datetime(2022, 7, 18, 20, 20, 45, 947941, tzinfo=datetime.timezone.utc)
In [81]: pendulum.now().utcnow() # without gotchas
Out[81]: DateTime(2022, 7, 18, 20, 20, 48, 501773, tzinfo=Timezone('UTC'))
Fetch a timezone-aware datetime. Here's an example:
>>> from datetime import datetime
>>> datetime.now() # timezone unaware, shows my local time
datetime.datetime(2022, 7, 18, 13, 16, 50, 249012)
>>> from datetime import timezone
>>> datetime.now(tz=timezone.utc) # timezone aware, UTC
datetime.datetime(2022, 7, 18, 20, 17, 28, 760889, tzinfo=datetime.timezone.utc)
Converting from UTC to a particular time zone:
>>> import pytz
>>> now = datetime.now(tz=timezone.utc)
>>> now.astimezone(tz=pytz.timezone('US/Pacific'))
datetime.datetime(2022, 7, 18, 13, 18, 6, 929968, tzinfo=<DstTzInfo 'US/Pacific' PDT-1 day, 17:00:00 DST>)
When I tried to convert a datetime object into timestamp and convert it back the result if off by 53 minutes.
timestamp = datetime.datetime(2022, 5, 3, 18, 0, 0, 0, tzinfo=pytz.timezone('US/Pacific')).timestamp()
# timetamp = 1651629180
datetime.datetime.fromtimestamp(timestamp, tz=pytz.timezone('US/Pacific'))
# formmated_timestamp = datetime.datetime(2022, 5, 3, 18, 53, tzinfo=<DstTzInfo 'US/Pacific' PDT-1 day, 17:00:00 DST>)
somehow the result is off by 53 minutes. This is true for all other values as well
The issue seems to be at the .timestamp() part
From the pytz docs:
This library only supports two ways of building a localized time. The first is to use the localize() method provided by the pytz library. This is used to localize a naive datetime (datetime with no timezone information):
>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0))
>>> print(loc_dt.strftime(fmt))
2002-10-27 06:00:00 EST-0500
The second way of building a localized time is by converting an existing localized time using the standard astimezone() method:
>>> ams_dt = loc_dt.astimezone(amsterdam)
>>> ams_dt.strftime(fmt)
'2002-10-27 12:00:00 CET+0100'
Unfortunately using the tzinfo argument of the standard datetime constructors ''does not work'' with pytz for many timezones.
>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=amsterdam).strftime(fmt)
'2002-10-27 12:00:00 LMT+0020'
I am currently trying to convert times from UTC and the problem that i am having is that the offsets seem to be backwards. As you can see when i convert the UTC to EST, it shows an offset of -4:56 yet when i print the time, it seems to add 4:56 as opposed to the way it should be. I would really like to be able to convert a UTC time to any other timezone and have it display the local time there without the offset so the UTC here would be converted to something along the lines of 2019-03-06 9:12 EST.
>>> example.created
datetime.datetime(2019, 3, 6, 14, 8, 49, 841881, tzinfo=<UTC>)
>>> original_utc = example.created
>>> original_utc
datetime.datetime(2019, 3, 6, 14, 8, 49, 841881, tzinfo=<UTC>)
>>> conv_est = original_utc.replace(tzinfo=pytz.timezone('US/Eastern'))
>>> conv_est
datetime.datetime(2019, 3, 6, 14, 8, 49, 841881, tzinfo=<DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>)
>>> print(conv_est)
2019-03-06 14:08:49.841881-04:56
>>> print(conv_est.astimezone())
2019-03-06 19:04:49.841881+00:00
I suspect that you misunderstood the method .astimezone().
Your original datetime is in UTC
>>> example.created
datetime.datetime(2019, 3, 6, 14, 8, 49, 841881, tzinfo=<UTC>)
Then you changed the timezone info for the variable conv_est, and indeed it works as designed:
>>> conv_est = original_utc.replace(tzinfo=pytz.timezone('US/Eastern'))
>>> conv_est
datetime.datetime(2019, 3, 6, 14, 8, 49, 841881, tzinfo=<DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>)
If you print this variable, it shows the correct info
>>> print(conv_est)
2019-03-06 14:08:49.841881-04:56
But when you call .astimezone() without any argument, then the return value is a datetime object in UTC zone; that means the method is also working as designed, returning the same point in time but expressed as localtime in UTC (It will be 7PM/19hs in UTC when it is 2PM/14hs in US/Eastern).
>>> print(conv_est.astimezone())
2019-03-06 19:04:49.841881+00:00
You can test that yourself by calculating the difference (which will be 0):
>>> conv_est == conv_est.astimezone()
True
>>> conv_est - conv_est.astimezone()
datetime.timedelta(0)
I'm probably missing something about timezones:
>>> import datetime, pytz
>>> date = datetime.datetime(2013,9,3,16,0, tzinfo=pytz.timezone("Europe/Paris"))
>>> date.astimezone(pytz.UTC)
datetime.datetime(2013, 9, 3, 15, 51, tzinfo=<UTC>)
I was expecting
datetime.datetime(2013, 9, 3, 15, 00, tzinfo=<UTC>)
Can anyone explain me where these 51 minutes come from?
Thanks,
Jean-Philippe
The UTC offset gives (date.tzinfo.utcoffset(date)):
datetime.timedelta(0, 540)
This is 540 seconds or 9 minutes.
In France the switch to UTC was made on March 11, 1911 and the clocks were turned back 9 minutes and 21 seconds (source 1, source 2):
Until 1911, Paris was 9 minutes and 21 seconds off UTC.
You can also see it here (Paris time in 1911) where the time goes from March 11, 12:01:00 AM to March 10, 11:51:39 PM.
Read the note at the very begining of pytz documentation ; use .localize() method to create timezone-aware datetime object:
import datetime
import pytz
naive_dt = datetime.datetime(2013,9,3,16,0)
dt = pytz.timezone("Europe/Paris").localize(naive_dt, is_dst=None)
to_s = lambda d: d.strftime('%Y-%m-%d %H:%M:%S %Z%z')
print(to_s(dt))
print(to_s(dt.astimezone(pytz.utc)))
Output
2013-09-03 16:00:00 CEST+0200
2013-09-03 14:00:00 UTC+0000
I don't know why you are expecting 15:00 UTC here.
Thanks Simeon for your answer. It made me realize how shallow is my understanding of all of this. The following experimentations lost me a little more...
>>> import datetime, pytz
>>> date_paris = datetime.datetime(2013,9,3,16,0, tzinfo=pytz.timezone("Europe/Paris"))
>>> date_utc = datetime.datetime(2013,9,3,16,0, tzinfo=pytz.utc)
>>> date_paris.astimezone(pytz.utc)
datetime.datetime(2013, 9, 3, 15, 51, tzinfo=<UTC>)
>>> date_utc.astimezone(pytz.timezone("Europe/Paris"))
datetime.datetime(2013, 9, 3, 18, 0, tzinfo=<DstTzInfo 'Europe/Paris' CEST+2:00:00 DST>)
Why this 9 minutes offset shows up when converting in one direction but not the other? The following piece of code concentrate all disappointment:
>>> date_paris
datetime.datetime(2013, 9, 3, 16, 0, tzinfo=<DstTzInfo 'Europe/Paris' PMT+0:09:00 STD>)
>>> date_paris.astimezone(pytz.utc).astimezone(pytz.timezone("Europe/Paris"))
datetime.datetime(2013, 9, 3, 17, 51, tzinfo=<DstTzInfo 'Europe/Paris' CEST+2:00:00 DST>)
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Python datetime object show wrong timezone offset
import pytz, datetime
pytz.timezone("Asia/Calcutta")
prints the following:
< DstTzInfo 'Asia/Calcutta' HMT+5:53:00 STD >
Why it is not 05:30 hrs? I am in time zone America/Los_Angeles.
Time zones change over the years. According to http://www.prokerala.com/travel/timezones/Asia/Kolkata?mode=history the original offset for that zone was 5.88888888889 hours, or 5 hours 53 minutes. pytz will use the proper offset and nomenclature once you assign the zone to an actual date.
>>> tz = pytz.timezone("Asia/Calcutta")
>>> tz
<DstTzInfo 'Asia/Calcutta' HMT+5:53:00 STD>
>>> tz.localize(datetime.datetime(1901, 7, 10, 12, 0))
datetime.datetime(1901, 7, 10, 12, 0, tzinfo=<DstTzInfo 'Asia/Calcutta' HMT+5:53:00 STD>)
>>> tz.localize(datetime.datetime(2012, 7, 10, 12, 0))
datetime.datetime(2012, 7, 10, 12, 0, tzinfo=<DstTzInfo 'Asia/Calcutta' IST+5:30:00 STD>)