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

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

Related

Why is this time comparison off by 1?

I want to check if the us/pacific time is after 9am.
However, when I compare the times, it still says 'False' even after 9. At 10am it says 'True'
# get us/pacific time
import pytz
tz = pytz.timezone('US/Pacific')
# get the time now and put it in us/pacific
from datetime import datetime
tmp_date = tz.fromutc(datetime.utcnow())
# make a threshold that is the same but hour = 9
tmp_date_threshold = datetime(year=tmp_date.year,month=tmp_date.month,day=tmp_date.day,hour=9,tzinfo=tz)
# see what the times are
>>> tmp_date
datetime.datetime(2022, 6, 20, 6, 33, 9, 810387, tzinfo=<DstTzInfo 'US/Pacific' PDT-1 day, 17:00:00 DST>)
>>> tmp_date_threshold
datetime.datetime(2022, 6, 20, 9, 0, tzinfo=<DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>)
# compare (expected)
>>> tmp_date > tmp_date_threshold
False
# add 3 hours to make it after 9am
tmp_date = tmp_date + timedelta(hours=3)
# now after 9am
>>> tmp_date
datetime.datetime(2022, 6, 20, 9, 33, 9, 810387, tzinfo=<DstTzInfo 'US/Pacific' PDT-1 day, 17:00:00 DST>)
# compare again
# still false
>>> tmp_date > tmp_date_threshold
False
Howcome is my comparison still failing even through tmp_date is after 9?
And howcome is my timezone info different for each object even though I used the same tz object to make each?

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 compare same time in different time zone?

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

Difference of two timedelta objects, with timezones

I would like to calculate how many hours there are in a date interval: for example "2014.03.29-30" should give 47, because of the daylight savings.
My method is making two datetime objects, in the example the following:
datetime.datetime(2014, 3, 29, 0, 0, tzinfo=<DstTzInfo 'Europe/Budapest' LMT+1:16:00 STD>)
datetime.datetime(2014, 3, 30, 23, 59, tzinfo=<DstTzInfo 'Europe/Budapest' LMT+1:16:00 STD>)
return (date2-date1) + timedelta(minutes=1)
However, it gives "2 days, 0:00:00", which is not correct. How could I make a timedelta object which takes timezones and dst into account? Also, if there's a simpler solution for the whole problem, I'm open to it.
Thank you!
Before 1901-12-13 20:45:52 UTC, the 'Europe/Budapest' timezone was LMT+1:16:00 STD.
Currently, as of 2016-05-05, the 'Europe/Budapest' timezone is CET+2:00:00 DST.
If you use pytz's localize method, then pytz will choose the timezone (utcoffset and dstoffset) for 'Europe/Budapest' which is appropriate for the given naive datetime:
import datetime as DT
import pytz
tzone = pytz.timezone('Europe/Budapest')
date1 = tzone.localize(DT.datetime(2014, 3, 29, 0, 0), is_dst=None)
# datetime.datetime(2014, 3, 29, 0, 0, tzinfo=<DstTzInfo 'Europe/Budapest' CET+1:00:00 STD>)
In contrast, if you supply tzinfo=tzone directly to datetime.datetime, as in:
wrong_date1 = datetime.datetime(2014, 3, 29, 0, 0, tzinfo=tzone)
# datetime.datetime(2014, 3, 29, 0, 0, tzinfo=<DstTzInfo 'Europe/Budapest' LMT+1:16:00 STD>)
then the datetime.datetime incorrectly chooses the very first timezone associated with 'Europe/Budapest' regardless of whether or not that was the timezone in effect on 2014-3-29.
Therefore, when using pytz, always use tzone.localize to make naive datetimes timezone-aware:
import datetime as DT
import pytz
tzone = pytz.timezone('Europe/Budapest')
date1 = tzone.localize(DT.datetime(2014, 3, 29, 0, 0), is_dst=None)
date2 = tzone.localize(DT.datetime(2014, 3, 30, 23, 59), is_dst=None)
print(((date2-date1) + DT.timedelta(minutes=1)).total_seconds()/3600.)
# 47.0
Do not use tzinfo=tzone unless tzone is pytz.utc (or a timezone which is alway the same throughout its history.)
Where did the date 1901-12-13 20:45:52 UTC come from?
You can peek at a pytz timezone's utc transition times (and associated transition info) using its tzone._utc_transition_times and tzone._transition_info private attributes:
In [43]: [(utcdate, utcoffset, dstoffset, tzabbrev) for utcdate, (utcoffset, dstoffset, tzabbrev) in zip(tzone._utc_transition_times, tzone._transition_info)][:2]
Out[43]:
[(datetime.datetime(1, 1, 1, 0, 0),
datetime.timedelta(0, 4560),
datetime.timedelta(0),
'LMT'),
(datetime.datetime(1901, 12, 13, 20, 45, 52),
datetime.timedelta(0, 3600),
datetime.timedelta(0),
'CET')]
This shows that from the date 1-1-1 UTC to 1901-12-13 20:45:52 UTC the timezone abbreviation was LMT and the utcoffset was 4560 seconds, which equals 1 hour and 16 minutes:
In [47]: print(DT.timedelta(0, 4560))
1:16:00
Hence the first timezone associated with 'Europe/Budapest' is LMT+1:16:00 STD.

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