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
%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")
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
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
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
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
2013-09-27T19:35:00-0500, which almost matches the example W3C Datetime format of
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!
By Mike Mclain