Posted:9/28/2013 3:13PM
Mike Mclain discusses Python 2.7.X and Time Zone Name Conversion with the datetime module
I was recently working on a site-map generator for my little python web system and during the process (of reading about the sitemap protocol) I discovered that the time standard utilized by the protocol was based upon the W3C Datetime format.
While I will admit that both Datetime formats and Regular expressions have a nasty tendency to make me snarl (if not swear) and (as expected) implementing this particular attribute into my site-map generator turned out to be rather painful.
Now, I know what you are thinking... Mike, why on earth would this task be hard?!? Python has a assortment of modules that can easily achieve this simplistic task!
To which I would reply... Alas, when I originally implemented my web system I designed it to allow the user to specify the date by the following encoding scheme %m/%d/%Y %I:%M%p
(I did this to allow backdating and to keep the transition between scrivener and the web system a metaphoric one way street), thus I opted to encode the datetime as %Y-%m-%d %H:%M:%S
(to allow for date sorting in sqlite) rather than the W3C Datetime format, since (once again) timestamps make me snarl and I just wanted something that was not overly complicated.
Well, a nasty problem arose upon attempting to convert my simplistic date format into the expected W3C Datetime format, since the W3C Format is YYYY-MM-DDThh:mm:ssTZD
and I had used Y-%m-%d %H:%M:%S
thus (upon attempting to utilize the date conversion %Y-%m-%dT%H:%M:%S%z
) the %z
or Time Zone Name variable was returning null.
At this point I thought to myself, O.K., the Time Zone Name variable was not defined so no default value was assumed by python hence why it is reporting null (I will admit that I was hoping for something non-null to be assumed by python here). Likewise the python time manual seems to validate this assessment.
Nevertheless, upon recognizing this particular attribute, I decided that I would simply augment the conversion string with a added Time Zone abbreviation variable --- yep a hack ---, reconvert the datetime into the W3C Format, and keep on trucking like so.
mydate = datetime.strptime(row["sdate"]+" est","%Y-%m-%d %H:%M:%S %Z") w3date=mydate.strftime("%Y-%m-%dT%H:%M:%S%z")
Where row["sdate"]
is in %Y-%m-%d %H:%M:%S
format.
Well, initially this idea seem to be working (at least until I started getting does not match format '%Y-%m-%d %H:%M:%S %Z'
errors) and attempts to look up valid terms for %Z
was far from fruitful (as it appears that this term is a local time zone variable or OS dependent variable, and there are a number of complaints on the web about this particular module in general).
Thus, after a hour of searching the web and finding nothing, I got frustrated and decided to dig into the \lib\_strptime.py
source module to find a answer by adding the following code:
for tz_names in self.locale_time.timezone: for tz in tz_names: print tz
to the def __init__(self):
function and see what values of %Z
I got back. Well, upon running my added code I got the following values of %Z
back (at least for my computer and python distribution):
and upon trying to convert the string YYYY-MM-DDTHH:MM:SS eastern standard time
, via:
mydate = datetime.strptime(row["sdate"]+" eastern standard time","%Y-%m-%d %H:%M:%S %Z") w3date=mydate.strftime("%Y-%m-%dT%H:%M:%S%z")
the function successfully runs (as in it does not throw an error); however it fails to encode the time correctly as the %z
of strftime
is still printed as null!
Likewise, this is a very interesting observation because strptime
will accept a named %Z
by giving it a valid tz
index within _strptime
but this index fails to be associated with a valid tzinfo
upon invoking a struct_time
object via:
return (time.struct_time((year, month, day, hour, minute, second,weekday, julian, tz)), fraction)
So clearly, this basic python function need some improvement (something I am not willing to do myself) to either correctly load timezone information or fail if no tzinfo
was associated with the struct_time
. Yes I know most people would recommend a solution from either dateutil
or pytz
but (at this point) I was fueled by principle to make this approach work...
Yet on a more sane note, if you are planning on using the strptime
command to perform a date conversion on data that has time zone information, I recommend you seek another module to perform this task (you might want to give pytz
some consideration)!
Also note, if you are curious as to what values of %Z
are supported on your machine and (unlike me) do not feel like editing the python core, the following code will print this information without requiring any python core modifications:
from _strptime import LocaleTime locale_time = LocaleTime() for tz_names in locale_time.timezone: for tz in tz_names: print tz
To continue this discussion further, once I figured out that the timezone information fails to be associated with a valid tzinfo
object (since apparently the online consensuses is datetime is deliberately a dumb object and needs a manual tzinfo
assignment) I started looking for information about how to associate a tzinfo
with a datetime.
After searching the web and the python manuals the following code manifested itself:
from datetime import datetime,tzinfo,timedelta class TimeZoneInfo(tzinfo): def __init__(self,offset,isdst,name): self.offset = offset self.isdst = isdst self.name = name def dst(self, dt): return timedelta(hours=1) if self.isdst else timedelta(0) def utcoffset(self, dt): return timedelta(hours=self.offset) + self.dst(dt) def tzname(self,dt): return self.name BlogTime = TimeZoneInfo(-5,False,'BlogTime') pydate = datetime.strptime(row["sdate"],"%Y-%m-%d %H:%M:%S") pydate = pydate.replace(tzinfo=BlogTime)
and upon running this code:
w3date=pydate.strftime("%Y-%m-%dT%H:%M:%S%z") print w3date
Results in 2013-09-27T19:35:00-0500
, which almost matches the example W3C Datetime format of 1997-07-16T19:20:30+01:00
.
So now that things are getting close to being correct (as 95% of programming is easy but the last 5% that will kill you) and given that the datetime module only supports timezone name %Z
and the UTC offset %z
, some slight modification to the code above is required to make everything W3C compliant.
Thus was this unholy string hack born:
w3date_A=pydate.strftime("%Y-%m-%dT%H:%M:%S") w3date_B=pydate.strftime("%z") w3date=w3date_A+ w3date_B[:3] + ':' + w3date_B[3:]
and this folks is why I hate timestamps! In conclusion, I do not recommend utilizing this method unless (like me) you only needed to augment a time zone to an existing date else (if you need to do anything else) save yourself a ton of trouble and seek out a better datetime method!
Enjoy!
By Mike Mclain