How to compare same time in different time zone? - python

I have 2 timestamps with different time zones:
date_1_str = '20200520090000' # GMT+2
date_2_str = '20 May 2020, 07:00' # UTC
I convert them to a datetime:
from datetime import datetime
from dateutil import tz
date_1 = datetime.strptime(date_1_str, '%Y%m%d%H%M%S').replace(tzinfo=tz.tzoffset('GMT+2', 0))
date_2 = datetime.strptime(date_2_str, '%d %b %Y, %H:%M').replace(tzinfo=tz.tzoffset('UTC', 0))
After that, I convert them to the local time zone to compare:
date_1_ = date_1.astimezone(tz.tzlocal())
# datetime.datetime(2020, 5, 20, 12, 0, tzinfo=tzlocal())
date_2_ = date_2.astimezone(tz.tzlocal())
# datetime.datetime(2020, 5, 20, 10, 0, tzinfo=tzlocal())
As you can see, it turned out not what I wanted, although in the case of UTC the conversion is correct. After conversion, both objects should be datetime.datetime(2020, 5, 20, 10, 0, tzinfo=tzlocal()) because my time zone is gmt+3 so 7.00 utc == 10.00 gmt+3 and 9.00 gmt+2 must be == 10.00 gmt+3
Where am I wrong?

both tzoffsets are 0 in your code; if you set them accordingly, you should get the correct output:
from datetime import datetime
from dateutil import tz
date_1_str = '20200520090000' # GMT+2
date_2_str = '20 May 2020, 07:00' # UTC
date_1 = datetime.strptime(date_1_str, '%Y%m%d%H%M%S').replace(tzinfo=tz.tzoffset('GMT+2', 2*3600))
date_2 = datetime.strptime(date_2_str, '%d %b %Y, %H:%M').replace(tzinfo=tz.tzoffset('UTC', 0))
# I'm on UTC+2, so the hour should stay the same:
date_1_ = date_1.astimezone(tz.tzlocal())
# datetime.datetime(2020, 5, 20, 9, 0, tzinfo=tzlocal())
# UTC hour should be +2:
date_2_ = date_2.astimezone(tz.tzlocal())
# datetime.datetime(2020, 5, 20, 9, 0, tzinfo=tzlocal())

Related

datetime.combine with timezone is different from datetime.now with timezone

In the below code:
from datetime import datetime
import pytz
EDT = pytz.timezone('US/Eastern')
d1 = datetime.now(tz=EDT)
d2 = datetime.combine(d1.date(), d1.time(), tzinfo=EDT)
Why are d1 and d2 showing different timezone information?
>> d1
datetime.datetime(2021, 4, 8, 7, 0, 44, 316514, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)
>> d2
datetime.datetime(2021, 4, 8, 7, 0, 44, 316514, tzinfo=<DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>)
How do I get the same datetime as datetime.now when using datetime.combine?
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

datetime.now(tz) and datetime(year, month, day, tzinfo=tz) don't have same UTC offset

Why are the UTC offsets in the 2 datetimes created in the code below different? I'm using pytz==2019.1
from datetime import datetime
import pytz
EASTERN = pytz.timezone('US/Eastern')
dt1 = datetime.now(EASTERN)
dt2 = datetime(2020, 8, 11, 15, 30, tzinfo=EASTERN)
print(f'dt1: {dt1}')
print(f'dt2: {dt2}')
results:
dt1: 2020-08-11 18:35:47.984745-04:00
dt2: 2020-08-11 15:30:00-04:56
The first one shows an UTC offset of -04:00 which is correct (for this time of year), but the 2nd one is giving an UTC offset of -04:56.
How can I declare a datetime with the 2nd method and get the UTC offset from the first method.
The issue with the shown code is that datetime.now(tz) can handle a pytz.timezone as tz argument while the default constructor datetime(y,m,d,...) cannot. In this case, you have to use the localize method of the timezone,
from datetime import datetime
import pytz
EASTERN = pytz.timezone('US/Eastern')
dt1 = datetime.now(EASTERN)
dt2 = EASTERN.localize(datetime(2020, 8, 11, 15, 30))
print(f'dt1: {dt1}')
print(f'dt2: {dt2}')
# prints
# dt1: 2020-08-12 10:07:09.738763-04:00
# dt2: 2020-08-11 15:30:00-04:00
dateutil avoids this issue, more info can be found here. That would make the code work "as it is":
from dateutil.tz import gettz
EASTERN = gettz('US/Eastern')
dt1 = datetime.now(EASTERN)
dt2 = datetime(2020, 8, 11, 15, 30, tzinfo=EASTERN)
print(f'dt1: {dt1}')
print(f'dt2: {dt2}')
# prints e.g.
# dt1: 2020-08-12 10:20:07.796811-04:00
# dt2: 2020-08-11 15:30:00-04:00
In addition pytz is likely to be deprecated with Python 3.9 as you'll have zoneinfo as part of the standard library for these kinds of jobs.
with regards to dt1:
datetime.now() will return the tzinfo from utc. You can see this if you do the following:
>>> repr(EASTERN.fromutc(datetime.now()))
"datetime.datetime(2020, 8, 11, 12, 33, 28, 849873, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)"
the tz.fromutc() is what datetime.now() does behind the scenes.
with regards to dt2:
if you check the repr of EASTERN you will see the following:
>>> repr(EASTERN)
"<DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>"
>>> repr(dt2)
"datetime.datetime(2020, 8, 11, 15, 30, tzinfo=<DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>)"
I'm not very comfortable with timezone differences so I don't want to steer you in the wrong direction, but from what I can tell both of these two options yield the same result:
dt2 = datetime(2020, 8, 11, 15, 30, tzinfo=EASTERN)
dt2_eastern_corrected = EASTERN.fromutc(dt2)
dt3 = datetime(2020, 8, 11, 15, 30)
dt3_eastern_corrected = EASTERN.fromutc(dt3
>>> repr(dt2_eastern_corrected)
"datetime.datetime(2020, 8, 11, 11, 30, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)"
>>> repr(dt3_eastern_corrected)
"datetime.datetime(2020, 8, 11, 11, 30, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)"

How to get the UTC + x number, according to local timezone and daylight saving?

How to get the UTC + x number, according to local timezone and daylight saving?
Example: let's say we're in Paris/France timezone. For a winter datetime (January 1st, 00:55:23), we expect 0001:
import datetime
print datetime.datetime(2018, 01, 01, 00, 55, 23).strftime('%z')
# empty string because the datetime is naive
# expecting 0001
For a summer datetime, we expect 0002:
print datetime.datetime(2018, 07, 01, 00, 55, 23).strftime('%z')
# expecting 0002
Using dateutil:
from dateutil import tz
from datetime import datetime
paris = tz.gettz('Europe/Paris')
dt1 = datetime(2018, 1, 1, 0, 55, 23, tzinfo=paris)
dt2 = datetime(2018, 7, 1, 0, 55, 23, tzinfo=paris)
print(dt1.strftime('%z')) # prints +0100
print(dt2.strftime('%z')) # prints +0200
Alternatively, using pytz:
from pytz import timezone
from datetime import datetime
paris = timezone('Europe/Paris')
dt1 = paris.localize(datetime(2018, 1, 1, 0, 55, 23))
dt2 = paris.localize(datetime(2018, 7, 1, 0, 55, 23))
print(dt1.strftime('%z')) # prints +0100
print(dt2.strftime('%z')) # prints +0200

Get UNIX timestamp (ie UTC timestamp) from a Python timezone aware date

My computer is running in Pacific time (hence datetime.datetime.fromtimestamp(0) gives me 1969-12-31 16:00:00). My problem is that given a timezone aware datetime object in Python, I want to get the UNIX timestamp (ie the UTC timestamp). What is the best way to do so?
import calendar
import datetime
import pytz
d = datetime.datetime.now(pytz.timezone('America/Los_Angeles'))
# d == datetime.datetime(2012, 7, 10, 1, 6, 36, 37542, tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>)
ts = calendar.timegm(d.utctimetuple())
# ts == 1341907596
# test with UTC epoch:
d = datetime.datetime(1969, 12, 31, 16, 0, 0, 0, pytz.timezone('America/Los_Angeles'))
# d == datetime.datetime(1969, 12, 31, 16, 0, tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
ts = calendar.timegm(d.utctimetuple())
# ts == 0

inconsistency when switching between timezones in python

I have a datetime object created like this:
tm = datetime.datetime.strptime('2010 Aug 04 14:15:16', '%Y %b %d %H:%M:%S')
>>> tm
datetime.datetime(2010, 8, 4, 14, 15, 16)
I then set the timezone like this:
tm.replace(tzinfo=pytz.timezone('UTC'))
>>> tm
datetime.datetime(2010, 8, 4, 14, 15, 16, tzinfo=<UTC>)
Eventually, I change the timezone to US/Pacific like this:
>>> tm = tm.astimezone(pytz.timezone('US/Pacific'))
>>> tm
datetime.datetime(2010, 8, 4, 7, 15, 16, tzinfo=<DstTzInfo 'US/Pacific' PDT-1 day, 17:00:00 DST>)
This is correct. But if I were to go into the other direction:
>>> tm = tm.replace(tzinfo =pytz.timezone('US/Pacific'))
>>> tm
datetime.datetime(2010, 8, 4, 14, 15, 16, tzinfo=<DstTzInfo 'US/Pacific' PST-1 day, 16:00:00 STD>)
As you can see, using replace() with the PST timezone incorrectly assumes it is not daylight savings time. Therefore, when I convert to UTC, it adds 8 hours instead of 7.
Why is it doing this and how do I change it so it does what I want (correctly realize DST or not)?
Yes, this is why you use .astimezone and not .replace when you have a datetime with a timezone. Using .astimezone gives the timezone a chance to adjust for things like daylight savings. Only use .replace to give a naïve datetime a tzinfo object.

Categories

Resources