Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
454 views
in Technique[技术] by (71.8m points)

python - Whats wrong with my datetime.strptime format?

With this function:

timestamp = datetime.strptime(date_str, date_fmt)

I am getting this error:

ValueError: time data 'Sun, 28 Oct 2018 07:33:13 -0400 (EDT)' does not match format '%a, %d %b %Y %H:%M:%S %z (%Z)'

Sun, 28 Oct 2018 07:33:13 -0400 (EDT)
%a, %d %b %Y %H:%M:%S %z (%Z)

I've looked over it a dozen times and I can't figure out what I am doing wrong.

My Python Version:

Python 3.7.0 (default, Jul 23 2018, 20:24:19) 
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...