There is no support for %Z
on input for anything other than your current timezone. Only the strings in the time.tzname
tuple plus 'UTC'
and 'GMT'
will ever be recognised:
>>> from datetime import datetime
>>> import time
>>> time.tzname
('GMT', 'BST')
>>> sample = 'Sun, 28 Oct 2018 07:33:13 -0400 (EDT)'
>>> datetime.strptime(sample.replace('EDT', time.tzname[0]), '%a, %d %b %Y %H:%M:%S %z (%Z)')
datetime.datetime(2018, 10, 28, 7, 33, 13, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=72000), 'GMT'))
>>> datetime.strptime(sample.replace('EDT', time.tzname[1]), '%a, %d %b %Y %H:%M:%S %z (%Z)')
datetime.datetime(2018, 10, 28, 7, 33, 13, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=72000), 'BST'))
Even if you use a 'known' timezone name in your input, the name is ignored unless there is also an timezone offset (%z
) in the input; with a %z
offset it is used only to set the name
argument of the datetime.timezone()
instance that's constructed from the %z
offset.
This behaviour is, unfortunately, not clearly documented, see Python issues #22377 and #22426. The documentation may seem to imply that EST
would be an acceptable value for %Z
to parse, but that section of the documentation only shows datetime.strftime()
string outputs, not acceptable datetime.strptime()
string inputs.
Since you also have the offset from UTC in the string (the -0400
part), just remove the timezone name from your input and don't bother trying to parse it:
>>> datetime.strptime(sample.rpartition(' ')[0], '%a, %d %b %Y %H:%M:%S %z')
datetime.datetime(2018, 10, 28, 7, 33, 13, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=72000)))
I used str.rpartition()
here to remove everything starting at the last space, but it depends on what kind of inputs that you have how to best remove the timezone name part in your application. Without it, the (%Z)
section is not needed and you get a correct datetime
object.
When debugging datetime.strptime()
issues, you want to compartmentalise the issue. You can bisect the input and template parts to see where the issue lies, or try out the different components one by one. It could be a single directive that causes the issue, or multiple, so I usually go for single-step removing of directives until one works.
For those interested in the nitty gritty details of how datetime.strptime()
works, you can step into the call with a debugger as the implementation uses a pure Python module. For datetime.strptime()
, the entry point is _strptime._strptime_datetime()
, and the %Z
parameter matching pattern is generated by this little loop, where self.locale_time.timezone
is a 2-value tuple of frozensets with 3-letter strings, which was set by the LocaleTime.__calc_timezone()
method.