Created
October 22, 2017 20:08
-
-
Save pglezen/233c2228e65655938e53c0037c4de029 to your computer and use it in GitHub Desktop.
Parsing timestamps with Z in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Parsing Date/Time Strings with Z\n", | |
"\n", | |
"The `datetime.strptime()` function does not handle a standard\n", | |
"timezone indicator specified as a `Z` on the end, which stands\n", | |
"for universal time. The string `2017-10-23T22:15:00Z` is the\n", | |
"kind of timestamp I might receive from the GitHub API for a\n", | |
"commit time." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 40, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"2017-10-23 22:15:00\n" | |
] | |
} | |
], | |
"source": [ | |
"from datetime import datetime\n", | |
"from datetime import timezone\n", | |
"\n", | |
"xml_str_wz = '2017-10-23T22:15:00Z'\n", | |
"xml_datetime_fmt = '%Y-%m-%dT%H:%M:%SZ'\n", | |
"\n", | |
"dt1 = datetime.strptime(xml_str_wz, xml_datetime_fmt)\n", | |
"print(dt1)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"One thing that is suspicious about this commit timestamp\n", | |
"for me is that I don't often commit changes past 10 pm;\n", | |
"I'm more of an early person. But the `Z` on the end is\n", | |
"timezone information which I ignored during the parse.\n", | |
"In particular, `Z` means UTC time. I can impose this\n", | |
"manually." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 41, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"2017-10-23 22:15:00+00:00\n" | |
] | |
} | |
], | |
"source": [ | |
"dt1 = dt1.replace(tzinfo=timezone.utc)\n", | |
"print(dt1)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Now that this object contains timezone information,\n", | |
"I can render it in my local time zone." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 42, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"2017-10-23 15:15:00-07:00\n" | |
] | |
} | |
], | |
"source": [ | |
"print(dt1.astimezone())" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"3:15 pm is much more like it.\n", | |
"\n", | |
"The `Z` is part of the ISO specification; but for some reason\n", | |
"there is no\n", | |
"[Python parse character](https://docs.python.org/3.5/library/datetime.html#strftime-and-strptime-behavior)\n", | |
"to handle it. The `%z` comes closest, but it demands explicit offsets." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 43, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"time data '2017-10-23T22:15:00Z' does not match format '%Y-%m-%dT%H:%M:%S%z'\n" | |
] | |
} | |
], | |
"source": [ | |
"try:\n", | |
" dt2 = datetime.strptime(xml_str_wz, '%Y-%m-%dT%H:%M:%S%z')\n", | |
" print(dt2.astimezone())\n", | |
"except ValueError as valueErr:\n", | |
" print(valueErr)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"But it works if the explicit offset is provided." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 44, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"2017-10-23 15:15:00-07:00\n" | |
] | |
} | |
], | |
"source": [ | |
"dt2 = datetime.strptime('2017-10-23T22:15:00+0000', '%Y-%m-%dT%H:%M:%S%z')\n", | |
"print(dt2.astimezone())" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"When I expect a datetime with a `Z` on the end, I provide a\n", | |
"function to parse it like this." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 45, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"('NULL',\n", | |
" 'NULL',\n", | |
" 'NULL',\n", | |
" datetime.datetime(2017, 10, 23, 22, 15, tzinfo=datetime.timezone.utc))" | |
] | |
}, | |
"execution_count": 45, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"def parseTimeZ(timeStrZ):\n", | |
" xml_fmt = '%Y-%m-%dT%H:%M:%SZ'\n", | |
" null_response = 'NULL'\n", | |
" if timeStrZ == None:\n", | |
" timeStrZ = ''\n", | |
" try:\n", | |
" result = datetime.strptime(timeStrZ, xml_fmt).replace(tzinfo=timezone.utc)\n", | |
" except ValueError:\n", | |
" result = null_response\n", | |
" return result\n", | |
"\n", | |
"(parseTimeZ(None), \n", | |
" parseTimeZ(''), \n", | |
" parseTimeZ('2017-10-23T22:15:00'), \n", | |
" parseTimeZ('2017-10-23T22:15:00Z'))" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python (p3)", | |
"language": "python", | |
"name": "p3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.5.3" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 1 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment