Daylight time saving aware conversion of timestamps in python - python

Given a timestamp without time zone (e.g. 2018-03-12 09:30:00) AND the timezone EST5EDT, the goal is to parse the data returning a datetime object that is time zone AND daylight saving aware.
from datetime import datetime
import pytz
datetime(2018, 3, 8, 9, 30, tzinfo=pytz.timezone('EST5EDT')).astimezone(pytz.utc)
# returns:
# datetime.datetime(2018, 3, 8, 14, 30, tzinfo=<UTC>)
datetime(2018, 3, 12, 9, 30, tzinfo=pytz.timezone('EST5EDT')).astimezone(pytz.utc)
# returns:
# datetime.datetime(2018, 3, 12, 14, 30, tzinfo=<UTC>)
# BUT should return (second Sunday of march the daylight saving changes by 1 hour):
# datetime.datetime(2018, 3, 12, 13, 30, tzinfo=<UTC>)

Never set tzinfo directly when creating datetimes. Always use the localize() method of the timezone (see the note at the top of http://pytz.sourceforge.net/):
pytz.timezone('EST5EDT').localize(
datetime(2018, 3, 12, 9, 30)
).astimezone(pytz.utc)

Related

Datetime print time without offset

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)

Why isn't my code working to make datetime objects timezone-aware?

I have two python datetime objects that represent the same moment in time:
a = datetime.datetime(2018, 10, 28, 13, 26, 30)
b = datetime.datetime(2018, 10, 28, 7, 26, 30)
Both are coming from different sources.
I know that the first is in UTC, and the second is in "America/Edmonton" (MDT).
Neither initially have a timezone attached to them.
I need to add timezones to these objects and compare them in a way where a == b is True.
What I did was this:
import datetime
from pytz import timezone
a = datetime.datetime(2018, 10, 28, 13, 26, 30)
b = datetime.datetime(2018, 10, 28, 7, 26, 30)
a = a.replace(tzinfo=timezone("UTC"))
b = b.replace(tzinfo=timezone("America/Edmonton"))
a = a.astimezone(timezone("America/Edmonton"))
b = b.astimezone(timezone("America/Edmonton"))
print(repr(a))
# Result: datetime.datetime(2018, 10, 28, 7, 26, 30, tzinfo=<DstTzInfo 'America/Edmonton' MDT-1 day, 18:00:00 DST>)
print(repr(b))
# Result: datetime.datetime(2018, 10, 28, 7, 26, 30, tzinfo=<DstTzInfo 'America/Edmonton' LMT-1 day, 16:26:00 STD>)
a == b # Results in False for some reason
What is "MDT-1 day, 18:00:00 DST" vs "LMT-1 day, 16:26:00 STD"? Why are they different? What am I doing wrong?
The proper way to do this appears to be:
import datetime
from pytz import timezone
a = datetime.datetime(2018, 10, 28, 13, 26, 30)
b = datetime.datetime(2018, 10, 28, 7, 26, 30)
a = timezone('UTC').localize(a)
b = timezone('America/Edmonton').localize(b)
a == b
As demonstrated here. This does result in a being equal to b. Still not sure why it sounds like pytz is defaulting to using a system from before 1893.

Np.interp doesn't work correctly on timestamps during American daylight savings transitions

These times are from Queensland, Australia where Daylight savings is not observed.
I have a program that interpolates time using this strategy, but it interpolates with respect to daylight savings time.
For example, this script interpolates between two time points with 100 intervals total.
import numpy as np
from datetime import datetime
import datetime as dt
import time
x_dec = np.linspace(0, np.pi, num=100)
time1 = dt.datetime(2017, 11, 4, 20, 47, 0)
time2 = dt.datetime(2017, 11, 5, 3, 1, 0)
this_time = time.mktime(time1.timetuple())
next_time = time.mktime(time2.timetuple())
this_x_temp = np.interp(x_dec, (x_dec.min(), x_dec.max()), (this_time, next_time))
this_x = np.vectorize(datetime.fromtimestamp)(this_x_temp)
print(this_x)
Instead of cleanly producing interpolated times, it cycles through times around 1AM twice in concordance with American Daylight Savings time. See the example output.
...
datetime.datetime(2017, 11, 5, 1, 49, 29, 90909)
datetime.datetime(2017, 11, 5, 1, 53, 52, 121212)
datetime.datetime(2017, 11, 5, 1, 58, 15, 151515)
datetime.datetime(2017, 11, 5, 1, 2, 38, 181818)
datetime.datetime(2017, 11, 5, 1, 7, 1, 212121)
datetime.datetime(2017, 11, 5, 1, 11, 24, 242424)
datetime.datetime(2017, 11, 5, 1, 15, 47, 272727)
...
I don't think that I can use a time series for this application given that all of my observations are at random times throughout the day and I need around 100 datapoints of interpolated times between the observations. How can I make np.interp ignore American daylight savings time and just interpolate the time as if it there is no daylight savings switch?
What you're seeing is US daylight savings time kicking in. At 2AM on 5 Nov 2017, it actually became 1AM again.
If you don't want to deal with US DST, I recommend using pytz to explicitly set your your time zone. It also might help to check what time zone your computer is set to, as datetime draws it's location and DST data from that. You can set it with
os.environ['TZ'] = 'Australia/Brisbane'
I think. Don't want to play around with my system too much

How to calculate timedelta between datetimes with different timezones in Python

How do you get a valid timedelta instance when differencing datetimes with different timezones in Python? I'm finding the timedelta is always 0 if the timezones are different.
>>> from dateutil.parser import parse
>>> dt0=parse('2017-02-06 18:14:32-05:00')
>>> dt0
datetime.datetime(2017, 2, 6, 18, 14, 32, tzinfo=tzoffset(None, -18000))
>>> dt1=parse('2017-02-06 23:14:32+00:00')
>>> dt1
datetime.datetime(2017, 2, 6, 23, 02, 12, tzinfo=tzutc())
>>> (dt1-dt0).total_seconds()
0.0
This doesn't make any sense to me. I would have thought that Python's datetime class would be smart enough to normalize both values to UTC internally, and then return a timedelta based on those values. Or throw an exception. Instead it returns 0, implying both datetimes are equal, which clearly they're not. What am I doing wrong here?
You are confused about what the timezone means; the two times you gave are identical, so of course their difference is zero. I can duplicate your results, except that I don't have the discrepancy between the second string and second datetime that you have:
>>> from dateutil.parser import parse
>>> dt0=parse('2017-02-06 18:14:32-05:00')
>>> dt0
datetime.datetime(2017, 2, 6, 18, 14, 32, tzinfo=tzoffset(None, -18000))
>>> dt1=parse('2017-02-06 23:14:32+00:00')
>>> dt1
datetime.datetime(2017, 2, 6, 23, 14, 32, tzinfo=tzutc())
>>> (dt1-dt0).total_seconds()
0.0
But watch what happens when I convert dt0 to UTC. The time gets adjusted by the 5 hour timezone difference, and it becomes identical to the second.
>>> dt0.astimezone(dt1.tzinfo)
datetime.datetime(2017, 2, 6, 23, 14, 32, tzinfo=tzutc())

Django Queryset Datetime Filter

I have a column formatted as such in one of my models:
TEMP_START = models.DateTimeField(null=True)
And I am attempting to do an exact lookup using queryset syntax such as
x.filter(TEMP_START=my_datetime_object) # x can be thought of as y.objects.all()
This returns no objects, when it should do more than one. However,
x.filter(TEMP_START__date=my_datetime_object.date()).filter(TEMP_START__hour=my_datetime_object.hour)
Does return the proper objects (they're hourly). Are direct datetime filters not supported, and thus keywords must be used?
====== Edit with bad results:
Searching for: {'TEMP_START': datetime.datetime(2016, 3, 31, 2, 0)}
Values in column: [{'TEMP_START': datetime.datetime(2016, 3, 29, 8, 0)}, {'TEMP_START': datetime.datetime(2016, 3, 29, 14, 0)}, {'TEMP_START': datetime.datetime(2016, 3, 30, 2, 0)}, {'TEMP_START': datetime.datetime(2016, 3, 29, 20, 0)}, {'TEMP_START': datetime.datetime(2016, 3, 30, 8, 0)}, {'TEMP_START': datetime.datetime(2016, 3, 30, 20, 0)}, {'TEMP_START': datetime.datetime(2016, 3, 31, 2, 0)}, {'TEMP_START': datetime.datetime(2016, 3, 30, 14, 0)}]
Values being returned: []
Code:
args_timeframe_start = {variables.temp_start: self.ranked_timeframes[rank][variables.temp_start]}
print(args_timeframe_start)
print(self.query_data.values(variables.temp_start).distinct())
query_data = self.query_data.filter(**args_timeframe_start)
print(query_data.values(variables.temp_start).distinct())
You need to find out what is my_datetime_object but most likely because DateTime fields contain python datetime.datetime objects, datetime.datetime objects is composed of year, month, date, hour, minute, second, microsecond. So if you merely compare date and hour, sure you could get results, but you couldn't guarantee that my_datetime_object matches one of the records in your database that has the same minute, second and microsecond.
Try this quickly and you could see what does datetime look like, also django doc about DateTimeField:
from datetime import datetime
date = datetime.now()
print date

Categories