How can I use an object, which is of type dateutil.tz.tz.tzoffset, to localize a timezone naive datetime object with correct timezone and DST info so it can be properly converted to UTC? Alternatively, how do I convert dateutil.tz.tz.tzoffset to a pytz.timezone?
I have been unable to find good information on localizing a datetime object using dateutil.tz.tz.tzoffset object. See below for a few of the better articles I've reviewed.
Background Info:
I am dealing with numerous date strings, most of which have no timezone information. In some cases the time is in GMT, in others it's local. I have to first determine the timezone of the device creating these various logs, then parse the various date strings, add the add timezone if it's a local time, and finally convert it to UTC.
I almost have this all working, except the only reliable way to determine the TimeZone is from a text file which has the date in the format of EDT, IST, etc so I have used the top up-voted post at the below link to accomplish this using dateutil's parser.parse() function and sending it a dict for it's tzinfos argument. (Parsing date/time string with timezone abbreviated name in Python?)
However, this leaves me with a DateTime which has a tzinfo type=dateutil.tz.tz.tzoffset. That's ok by me, except then I need to use this tzinfo to localize the strings that do not contain Timezone info, and the dateutil.tz.tz.tzoffset type does not have a localize option like pytz.timezone does, which is the crux of my problem.
Am I making this too hard? Do I just replace the tzinfo in the timezone naive datetime with the dateutil.tz.tz.tzoffset object I've saved?
Code:
Below reads a date string and saves it as a datetime object, saves the timezone in a var for later use, and then converts the datestring to UTC:
from dateutil.parser import parse as parsedate
import pytz
from pytz import timezone
from datetime import datetime
timestr = 'Sat, 5/01/2019 8:00PM EDT' #only reliable source of timezone info
dtfromstrEDT = parsedate(timestr, tzinfos=tzd) #tzd is created from the above link
mytimeZone = dtfromstrEDT.tzinfo # save local timezone
dtUTC = dtfromstrEDT.astimezone(pytz.timezone('UTC')) # convert to utc
Now here is a new timezone naive datestring. It was recorded in local time same as above (EDT) and so I want to use my saved mytimeZone var to localize it and convert to UTC. I use the standard strptime to read it in as a naive datetime. However, when using mytimeZone to localize the Naive datetime, it fails. Error below. I understand the error; but I don't know how to accomplish what I need:
timestrnaive = 'Mar 15 12:09:20 2019' #in EDT time, same as above string but without any timezone info
dtfromstrNaive = datetime.strptime(timestrnaive, "%b %d %H:%M:%S %Y")
dtlocalized = mytimeZone.localize(dtfromstrNaive, is_dst=True)
# the above is where it fails with provided error below
# however I can do this instead if I had a pytz.timezone object:
loc_tz = pytz.timezone('America/New_York')
dtlocalized = loc_tz.localize(dtfromstrNaive, is_dst=True)
dtUTC2 = dtlocalized.astimezone(pytz.timezone('UTC')) # convert to utc
Error:
Traceback (most recent call last):
File "C:\Examples\timezones.py", line 221, in <module>
dtlocalized = mytimeZone.localize(dtfromstrNaive, is_dst=True) # convert to whatever tz is stored in self.timeZone
AttributeError: 'tzoffset' object has no attribute 'localize'
Reviewed the following:
Localize datetime (timezone aware) from timezone offset - I am not sure how to apply that to this problem since I have a dateutil.tz.tz.tzoffset object, not the raw utc time.
Parsing date/time string with timezone abbreviated name in Python? - This got me where I could read timezone data such as EDT and PDT.
How to make an unaware datetime timezone aware in python - This does not help because it doesn't address the dateutil.tz.tz.tzoffset issue I mention.
The major misconception you are harboring here is that you need the localize
method, which is a historical artifact in pytz
harkening from an era before PEP 495 added the "fold" attribute. You can read more about the reason that pytz
deviates from the standard library's interface in this article.
As you note in your answer, for anything other than pytz
zones, you can simply use .replace
to construct a new datetime with the appropriate time zone. There is also a dateutil.utils.default_tzinfo
convenience function that will automatically detect if a datetime is naive or not and attach a default value to the tzinfo
if so.
One other thing to note is that your use of pytz
here is unnecessary, as dateutil
also provides a UTC
object, so you can do something like this:
from dateutil import tz
timestrnaive = 'Mar 15 12:09:20 2019'
dtfromstrNaive = datetime.strptime(timestrnaive, "%b %d %H:%M:%S %Y")
dtlocalized = dtfromstrNaive.replace(tzinfo=mytimeZone)
dtUTC2 = dtlocalized.astimezone(tz.UTC) # Convert to UTC
Thus you would not need to keep any dependency on pytz
.
If you want to learn more about using time zones in Python, I also recently gave a talk about this at PyCon.