{"id":17,"date":"2021-07-02T10:00:15","date_gmt":"2021-07-02T00:00:15","guid":{"rendered":"http:\/\/localhost:8000\/?p=17"},"modified":"2021-07-02T10:00:15","modified_gmt":"2021-07-02T00:00:15","slug":"python-date-time-intro","status":"publish","type":"post","link":"http:\/\/www.cheerfulprogramming.com\/?p=17","title":{"rendered":"Python Date Time Intro"},"content":{"rendered":"<p>This is a brief introduction to date and time in Python.<\/p>\n<h2>Definitions<\/h2>\n<p>Here are some commonly used terms:<\/p>\n<ul>\n<li><strong>Greenwich Mean Time<\/strong>: abbreviated GMT. This was the zero offset<br \/>\ntimezone. It has now been superseded by Universal Coordinated Time<br \/>\n(see next term) although the two terms are often used<br \/>\ninterchangeably. See<br \/>\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Greenwich_Mean_Time\">Wikipedia<\/a>.<\/li>\n<li><strong>Coordinated Universal Time<\/strong>: abbreviated UTC. This is zero offset<br \/>\ntimezone but should be used instead of GMT, because it is more<br \/>\nprecise than GMT, and not just to keep the French happy. See<br \/>\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Coordinated_Universal_Time\">Wikipedia<\/a>.<\/li>\n<\/ul>\n<h2>Storing date and time values<\/h2>\n<h3>Zone-Aware Date and Time<\/h3>\n<p>The <code class=\"\" data-line=\"\">zoneinfo<\/code> module uses your operating system\\&#8217;s time zone database if it is available, or the <code class=\"\" data-line=\"\">tzdata<\/code> Python package if it is unavailable.  Create a new <code class=\"\" data-line=\"\">ZoneInfo<\/code> object as follows and use it to get the current timestamp or a datetime of your choosing:<\/p>\n<pre><code class=\"\" data-line=\"\">from datetime import datetime\nfrom zoneinfo import ZoneInfo\n\nnow = datetime.now(ZoneInfo(&quot;Australia\/Sydney&quot;))\n# Start date of Le Tour de France:\ntdf_start_date = datetime(2021,6,28,9,0,0,0,ZoneInfo(&quot;Europe\/Paris&quot;))<\/code><\/pre>\n<p>A Python datetime object can alo be created by parsing an ISO datetime string, or a string in a <a href=\"https:\/\/docs.python.org\/3\/library\/datetime.html#strftime-strptime-behavior\">format of your choice<\/a>, according to C date and time formatting conventions:<\/p>\n<pre><code class=\"\" data-line=\"\"># End date of Le Tour de France, ISO style:\ntdf_end_date = datetime.fromisoformat(&quot;2021-07-18T18:00:00.000+02:00&quot;)\n# or in a custom format:\ntdf_end_date = datetime.strptime(&quot;18-07-2021 18:00:00.00 +0200&quot;, &quot;%d-%m-%Y %H:%M:%S.%f %z&quot;)<\/code><\/pre>\n<p>The Python standard library has only limited ISO datetime string parsing features, and is not fully compatible with ISO 8601.  Use third-party packages for full compatibility.<\/p>\n<p>The timezone of your <code class=\"\" data-line=\"\">now<\/code> object is stored in the <code class=\"\" data-line=\"\">tzinfo<\/code> attribute as shown: <\/p>\n<pre><code class=\"\" data-line=\"\">now.tzinfo\n# The utcoffset() method below returns a timedelta with a seconds attribute.\n# The next line prints 36000.\nnow.tzinfo.utcoffset(now).seconds<\/code><\/pre>\n<p>Here are some other useful methods you can call on your datetime object:<\/p>\n<pre><code class=\"\" data-line=\"\"># The weekday() method returns an integer number for the day of \n# the week, where Monday = 0.\nnow.weekday()\n# The isoformat() method returns a datetime string, \n# eg &quot;2021-07-02T17:18:12.646789+10:00&quot;\nnow.isoformat()\n# The dst() indicates whether daylight savings applies to \n# the datetime object.  It returns a timedelta with a seconds \n# attribute, which is zero during standard time.\nnow.dst()<\/code><\/pre>\n<h3>&quot;Naive&quot; (Zone-Unaware) Date and Time<\/h3>\n<p>If you don\\&#8217;t need to deal with offsets and regions then Python lets you omit them, and create &quot;naive&quot; <code class=\"\" data-line=\"\">datetime<\/code> objects:<\/p>\n<pre><code class=\"\" data-line=\"\">now = datetime.now()<\/code><\/pre>\n<p>Naturally, the <code class=\"\" data-line=\"\">tzinfo<\/code> attribute of such a datetime object will be <code class=\"\" data-line=\"\">None<\/code>:<\/p>\n<pre><code class=\"\" data-line=\"\">print(now.tzinfo)<\/code><\/pre>\n<h3>Date and Time<\/h3>\n<p>If you only need to deal with whole days then the <a href=\"https:\/\/docs.python.org\/3\/library\/datetime.html#datetime.date\"><code class=\"\" data-line=\"\">date<\/code><\/a> class is very handy. Similarly if you only need to deal with times, then Python provides the <a href=\"https:\/\/docs.python.org\/3\/library\/datetime.html#datetime.time\"><code class=\"\" data-line=\"\">time<\/code><\/a> class.<\/p>\n<pre><code class=\"\" data-line=\"\">from datetime import date, time, timezone\nfrom zoneinfo import ZoneInfo\ntoday = date.today()\ntdf_start_date = date(2021,6,28)\nnow_naive = time()\nnow_zoned = time(tzinfo=timezone.utc)\ntdf_start_time_naive = time(9,0,0,0)\ntdf_start_time_zoned = time(9,0,0,0,tzinfo=ZoneInfo(&quot;Europe\/Paris&quot;))<\/code><\/pre>\n<h2>Manipulating Dates and Times<\/h2>\n<p>Suppose you want to watch the final stage of Le Tour de France on Sunday 18<sup>th<\/sup> July 2021, sixteen days from today (at the time of writing), at 11:30 pm.  You could calculate it as follows:<\/p>\n<pre><code class=\"\" data-line=\"\">from datetime import datetime, timedelta\nfrom zoneinfo import ZoneInfo\nsixteen_days = timedelta(days=16)\ntdf_final_sydney = datetime.now(ZoneInfo(&quot;Australia\/Sydney&quot;)) \\\n                      .replace(hour=23,minute=30,second=0) \\\n                      + sixteen_days\ntdf_final_paris = tdf_final_sydney.astimezone(ZoneInfo(&quot;Europe\/Paris&quot;))<\/code><\/pre>\n<p>Line 3 creates a time difference object, a <a href=\"https:\/\/docs.python.org\/3\/library\/datetime.html#datetime.timedelta\"><code class=\"\" data-line=\"\">timedelta<\/code><\/a>.  Different units of time can be supplied as named arguments, and these objects can be used to perform date and time arithmetic with <code class=\"\" data-line=\"\">datetime<\/code> objects and with other <code class=\"\" data-line=\"\">timedelta<\/code> objects.  Line 4 gets today\\&#8217;s date in Sydney.  Line 5 sets the time to 11:30 pm.  Line 6 then uses the <code class=\"\" data-line=\"\">timedelta<\/code> to advance today\\&#8217;s date (Sydney time) sixteen days into the future, when the final will be.  Line 7 calculates what time the start will be in Paris by adjusting the timezone, so you know what time it will be for the riders.<\/p>\n<p>You can prove that two objects <code class=\"\" data-line=\"\">tdf_final_sydney<\/code> and <code class=\"\" data-line=\"\">tdf_final_paris<\/code>, which represent the start of the race in Sydney and Paris respectively, represent the same moment in time and have no time difference between them, by subtracting one from the other.  Open a Python console and create the two variables with the code above, then run the code below:<\/p>\n<pre><code class=\"\" data-line=\"\">&gt;&gt;&gt; tdf_final_sydney - tdf_final_paris\ndatetime.timedelta(0)<\/code><\/pre>\n<h2>Traps and Pitfalls<\/h2>\n<p>Zone unaware <code class=\"\" data-line=\"\">datetime<\/code>s sometimes can produce wrong values in some versions of Python if you try to add zone information to them in the wrong way.  eg:<\/p>\n<pre><code class=\"\" data-line=\"\">from datetime import datetime, timezone\nbad_apr01 = datetime(2022,4,1).astimezone(timezone.utc) # WRONG!\napr01 = datetime(2022,4,1, tzinfo=timezone.utc) # CORRECT<\/code><\/pre>\n<p>Similarly, be careful when obtaining the current moment.  Don&#8217;t use <code class=\"\" data-line=\"\">utcnow()<\/code> at all.<\/p>\n<pre><code class=\"\" data-line=\"\">from datetime import datetime, timezone\nnow_wrong = datetime.utcnow() # WRONG!  Is zone unaware!\nnow_correct = datetime.now(tz=timezone.utc) # CORRECT<\/code><\/pre>\n<p>If you use the <a href=\"https:\/\/pypi.org\/project\/pytz\/\">PyTz<\/a> third party timezone library, don&#8217;t use a PyTz timezone object as an argument to the <code class=\"\" data-line=\"\">datetime<\/code> constructor since it can give inconsistent results.  There is more information about this on the package information page on PyPi but here is the short version:<\/p>\n<pre><code class=\"\" data-line=\"\">import pytz\nfrom datetime import datetime\nsydney_tz = pytz.timezone(&quot;Australia\/Sydney&quot;)\nbad_apr01 = datetime(2022,4,1 tzinfo=sydney_tz) # WRONG!\napr01 = sydney_tz.localize(datetime(2022,4,1)) # CORRECT<\/code><\/pre>\n<h2>Further Reading<\/h2>\n<p><a href=\"https:\/\/docs.python.org\/3\/library\/datetime.html\">https:\/\/docs.python.org\/3\/library\/datetime.html<\/a><\/p>\n<p><a href=\"https:\/\/docs.python.org\/3\/library\/zoneinfo.html\">https:\/\/docs.python.org\/3\/library\/zoneinfo.html<\/a><\/p>\n<p><a href=\"https:\/\/pypi.org\/project\/pytz\/\">https:\/\/pypi.org\/project\/pytz\/<\/a><\/p>\n<h2>Acknowledgements<\/h2>\n<p>The author acknowledges the traditional custodians of the Daruk and the<br \/>\nEora People and pays respect to the Elders past and present.<\/p>\n<p>Python\u00ae is a registered trademark of the Python Software Foundation and\/or its<br \/>\naffiliates.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is a brief introduction to date and time in Python. Definitions Here are some commonly used terms: Greenwich Mean Time: abbreviated GMT. This was the zero offset timezone. It has now been superseded by Universal Coordinated Time (see next term) although the two terms are often used interchangeably. See Wikipedia. Coordinated Universal Time: abbreviated [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":32,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[8,9,15,17,18],"class_list":["post-17","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python","tag-calendar","tag-date","tag-python","tag-time","tag-timezone"],"_links":{"self":[{"href":"http:\/\/www.cheerfulprogramming.com\/index.php?rest_route=\/wp\/v2\/posts\/17","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.cheerfulprogramming.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.cheerfulprogramming.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.cheerfulprogramming.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.cheerfulprogramming.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=17"}],"version-history":[{"count":0,"href":"http:\/\/www.cheerfulprogramming.com\/index.php?rest_route=\/wp\/v2\/posts\/17\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.cheerfulprogramming.com\/index.php?rest_route=\/wp\/v2\/media\/32"}],"wp:attachment":[{"href":"http:\/\/www.cheerfulprogramming.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=17"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.cheerfulprogramming.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=17"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.cheerfulprogramming.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=17"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}