|
| 1 | +<!DOCTYPE html> |
| 2 | +<html> |
| 3 | +<head> |
| 4 | + <meta charset="utf-8"> |
| 5 | + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
| 6 | + <meta name="viewport" content="width=1024, user-scalable=no"> |
| 7 | + |
| 8 | + <title>Python300 week 06, datetime</title> |
| 9 | + |
| 10 | + <!-- Required stylesheet --> |
| 11 | + <link rel="stylesheet" media="screen" href="deckjs/core/deck.core.css"> |
| 12 | + |
| 13 | + <!-- Extension CSS files go here. Remove or add as needed. --> |
| 14 | + <link rel="stylesheet" media="screen" href="deckjs/extensions/goto/deck.goto.css"> |
| 15 | + <link rel="stylesheet" media="screen" href="deckjs/extensions/menu/deck.menu.css"> |
| 16 | + <link rel="stylesheet" media="screen" href="deckjs/extensions/navigation/deck.navigation.css"> |
| 17 | + <link rel="stylesheet" media="screen" href="deckjs/extensions/status/deck.status.css"> |
| 18 | + <link rel="stylesheet" media="screen" href="deckjs/extensions/scale/deck.scale.css"> |
| 19 | + |
| 20 | + <!-- Style theme. More available in /themes/style/ or create your own. --> |
| 21 | + <link rel="stylesheet" media="screen" href="deckjs/themes/style/swiss.css"> |
| 22 | + <!-- Transition theme. More available in /themes/transition/ or create your own. --> |
| 23 | + <link rel="stylesheet" media="screen" href="deckjs/themes/transition/horizontal-slide.css"> |
| 24 | + |
| 25 | + <!-- Basic black and white print styles --> |
| 26 | + <link rel="stylesheet" media="print" href="deckjs/core/print.css"> |
| 27 | + |
| 28 | + <!-- Required Modernizr file --> |
| 29 | + <script src="deckjs/modernizr.custom.js"></script> |
| 30 | +</head> |
| 31 | +<body> |
| 32 | + <div class="deck-container"> |
| 33 | + |
| 34 | + <!-- Begin slides. Just make elements with a class of slide. --> |
| 35 | + <section class="slide"> |
| 36 | + <h2>System Development with Python</h2> |
| 37 | + <h3>Week 6 :: datetime, time, pytz</h3> |
| 38 | + <p>Joseph Sheedy</p> |
| 39 | + |
| 40 | + <p>Git repository: <a href="https://github.com/UWPCE-PythonCert/Python300-SystemDevelopmentWithPython-Spring-2014" target="_blank">https://github.com/UWPCE-PythonCert/Python300-SystemDevelopmentWithPython-Spring-2014</a></p> |
| 41 | +</section> |
| 42 | + |
| 43 | +<section class="slide"> |
| 44 | + <h2>datetime, time, pytz</h2> |
| 45 | + <h3>What's so hard about representing time? </h3> |
| 46 | + <p>Humans are mostly accustomed to communicating in their local timezone. |
| 47 | + <p>There are hundreds of them, whose definitions change |
| 48 | + <p>What happens to your application when it's deployed to a server running in a different timezone? |
| 49 | + <p>The length of a day is not constant, and <a target="_blank" href="http://en.wikipedia.org/wiki/Leap_second">leap seconds</a> have been inserted frequently |
| 50 | + <p>Daylight savings time challenges |
| 51 | +</section> |
| 52 | + |
| 53 | +<section class="slide"> |
| 54 | + <h2><a target="_blank" href="http://en.wikipedia.org/wiki/Daylight_saving_time">DST</a></h2> |
| 55 | + <p>Daylight savings time creates discontinuities in a time series |
| 56 | + <p> |
| 57 | + <img width="90%" src="images/6libU.png" /> |
| 58 | + <p> |
| 59 | + <img width="90%" src="images/FfBuN.png" /> |
| 60 | +</section> |
| 61 | + |
| 62 | +<section class="slide"> |
| 63 | + <h2>On representing a point in time</h2> |
| 64 | + <ul> |
| 65 | + <li>There are two major time standards which you should understand: |
| 66 | + <ul> |
| 67 | + <li><a target="_blank" href="http://en.wikipedia.org/wiki/UTC">UTC: Coordinated Universal Time (French: Temps Universel Coordonné)</a> |
| 68 | + <li><a target="_blank" href="http://en.wikipedia.org/wiki/Unix_time">UNIX time</a> |
| 69 | + </ul> |
| 70 | + </ul> |
| 71 | +</section> |
| 72 | +<section class="slide"> |
| 73 | + <h2><a target="_blank" href="http://en.wikipedia.org/wiki/UTC">UTC: Coordinated Universal Time (French: Temps Universel Coordonné)</a></h2> |
| 74 | + <p>This is the primary time standard in the world |
| 75 | + <p>Provides a consistent reference all other time zones can relate to |
| 76 | + <p>No daylight savings time |
| 77 | + <p>Also known as Zulu Time, as the UTC time zone is sometimes denoted by the letter Z, e.g. 12:59Z |
| 78 | + <p>It is similar to, but not the same as, Greenwich Mean Time (GMT). UTC is more precisely defined |
| 79 | + <p> |
| 80 | +<!-- |
| 81 | +--> |
| 82 | + <img width="30%" src="images/Greenwich_clock.jpg" /> |
| 83 | +</section> |
| 84 | + |
| 85 | +<section class="slide"> |
| 86 | + <h2><a target="_blank" href="http://en.wikipedia.org/wiki/Unix_time">UNIX time</a></h2> |
| 87 | + <ul> |
| 88 | + <li>UNIX time is represented as the number of seconds from one point in time known as the Epoch, defined to be 00:00 UTC January 1, 1970, not counting leap seconds |
| 89 | + <li>Note that UNIX time is based on another time standard |
| 90 | + <li>On 32 bit OSes which store time as a signed 32-bit integer, the largest value that can be stored is 03:14:07 UTC on Tuesday, 19 January 2038. One second afterwards, time overflows back to zero. This is known as the Y2038 problem. |
| 91 | + <li>On 64 bit machines, the largest time which can be represented is in the year 292 billion, at which time our universe is predicted to not exist |
| 92 | + </ul> |
| 93 | +</section> |
| 94 | + |
| 95 | + |
| 96 | +<section class="slide"> |
| 97 | + <h2>timezones and datetime calculations</h2> |
| 98 | + <p> |
| 99 | + <li>Calculating the time between two dates spanning timezones, daylight savings time transitions, and leap seconds is a task fraught with error |
| 100 | + <li>The <a target="_blank" href="http://en.wikipedia.org/wiki/Tz_database">Olson database</a> is a reference database of the world's timezones. |
| 101 | + <li>It can be obtained through the IANA <a target="_blank" href="http://www.iana.org/time-zones">here</a> |
| 102 | + <li>As in <a target="_blank" href="https://docs.python.org/2.7/library/datetime.html">datetime docs</a>, <blockquote>The rules for time adjustment across the world are more political than rational</blockquote> |
| 103 | + </p> |
| 104 | +</section> |
| 105 | + |
| 106 | +<section class="slide"> |
| 107 | + <h2>from datetime import time, date, datetime</h2> |
| 108 | + <p>The datetime object represents a specific moment in time |
| 109 | + <p>The date object represents a calendar date |
| 110 | + <p>The time object represents a time |
| 111 | +<pre><code> |
| 112 | +from datetime import datetime |
| 113 | + |
| 114 | +t = datetime(2019, 11, 1) |
| 115 | +date = t.date() |
| 116 | + |
| 117 | +now = datetime.now() |
| 118 | +</code></pre> |
| 119 | +</section> |
| 120 | + |
| 121 | +<section class="slide"> |
| 122 | + <h2>limitations of datetime</h2> |
| 123 | + <ul> |
| 124 | + <li>precision is limited to microseconds |
| 125 | + <li>time range is limited: |
| 126 | +<pre><code> |
| 127 | +In [3]: datetime.MINYEAR |
| 128 | +Out[3]: 1 |
| 129 | + |
| 130 | +In [4]: datetime.MAXYEAR |
| 131 | +Out[4]: 9999 |
| 132 | + |
| 133 | +In [5]: datetime.datetime(37337,1,1) |
| 134 | +--------------------------------------------------------------------------- |
| 135 | +ValueError Traceback (most recent call last) |
| 136 | +<ipython-input-5-87a91e367d25> in <module>() |
| 137 | + |
| 138 | +ValueError: year is out of range |
| 139 | + |
| 140 | +In [6]: datetime.datetime(0,1,1) |
| 141 | +--------------------------------------------------------------------------- |
| 142 | +ValueError Traceback (most recent call last) |
| 143 | +<ipython-input-6-7e3894d0bf49> in <module>() |
| 144 | + |
| 145 | +ValueError: year is out of range |
| 146 | + |
| 147 | +</code></pre> |
| 148 | + <li>If you are on a 32-bit OS and using Python pre-2.6, you will be limited to the year 2038. |
| 149 | + |
| 150 | + </ul> |
| 151 | +</section> |
| 152 | + |
| 153 | +<section class="slide"> |
| 154 | + <h2>datetime -> string</h2> |
| 155 | + <p> |
| 156 | +<pre><code>from datetime import datetime |
| 157 | +t = datetime.now() |
| 158 | +t.isoformat() |
| 159 | +t.strftime("Date: %B %d, %Y. Time: %H:%M") |
| 160 | +</code></pre> |
| 161 | + <p>strftime passes format codes to the strftime of the platform's C library. This may not be standardized! A list of format directives is <a target="_blank" href="https://docs.python.org/2.7/library/datetime.html#strftime-strptime-behavior">here</a> |
| 162 | + <p>getting the current UNIX time : |
| 163 | +<pre><code>int(datetime.datetime.now().strftime('%s')) |
| 164 | +# in Python 3.3+: |
| 165 | +datetime.now().timestamp() |
| 166 | +</code></pre> |
| 167 | +</section> |
| 168 | + |
| 169 | +<section class="slide"> |
| 170 | + <h2>string -> datetime</h2> |
| 171 | + <p>datetime.strptime(string, format) |
| 172 | +<pre><code> |
| 173 | +In [88]: datetime.datetime.strptime? |
| 174 | +Type: builtin_function_or_method |
| 175 | +String form: <built-in method strptime of type object at 0x10f8536e0> |
| 176 | +Docstring: string, format -> new datetime parsed from a string (like time.strptime()). |
| 177 | + |
| 178 | +time_string = "2019/11/1 2300" |
| 179 | +format = "%Y/%m/%d %H%M" |
| 180 | +datetime.datetime.strptime(time_string, format) |
| 181 | +</code></pre> |
| 182 | + <p>strptime format strings use the same formatting tokens as strftime |
| 183 | + <p>If you need to parse arbitrary time strings, or don't want to maintain format strings, there is <a target="_blank" href="http://labix.org/python-dateutil">python-dateutil.parser</a> |
| 184 | + |
| 185 | +</section> |
| 186 | + |
| 187 | +<section class="slide"> |
| 188 | + <h2>two types of datetimes - naive and aware</h2> |
| 189 | + <p>so far we haven't created any datetime objects with associated timezone information, these are known as 'naive' datetimes |
| 190 | + <p>in order to accurately represent a real time, timezone information is required |
| 191 | + <p>Some systems may expect naive times to represent UTC, others may decide on local time zone. It's usually safer to be explicit. |
| 192 | + <p>Timezone is specified with the tzinfo attribute, through the constructor: datetimes are immutable |
| 193 | + <p>Change the timezone or another attribute with the replace(*args, **kwargs) method to create a new object with all the same attributes except those specified |
| 194 | + <p>tzinfo is expected to be a subclass of datetime.tzinfo |
| 195 | + <p>However, datetime.tzinfo is an abstract base class, which means you'll need to define your own subclass (filled with peril) or install the pytz package |
| 196 | + <p>pytz is based on the <a target="_blank" href="http://en.wikipedia.org/wiki/Tz_database">Olson database</a>, a reference database of the world's timezones. |
| 197 | +</section> |
| 198 | + |
| 199 | +<section class="slide"> |
| 200 | + <h2>pytz</h2> |
| 201 | +<p>A list of all the timezones in pytz is available in pytz.all_timezones |
| 202 | +<p>Timezone is often specified as a UTC offset in hours, e.g. 2019-11-1T11:59-08:00. Don't use this offset directly to permanently record a user's timezone because it may change with Daylight Savings Time. Better to obtain the IANA code from the list in pytz and let datetime do the calculations. |
| 203 | +<p> |
| 204 | +<pre><code> |
| 205 | +from datetime import datetime |
| 206 | + |
| 207 | +import pytz |
| 208 | + |
| 209 | +t1 = datetime(2019,11,1, tzinfo=pytz.UTC) |
| 210 | + |
| 211 | +us_pacific_tz = pytz.timezone('US/Pacific') |
| 212 | +t2 = datetime(2019,11,1, tzinfo=us_pacific_tz) |
| 213 | + |
| 214 | +</code></pre> |
| 215 | +</section> |
| 216 | + |
| 217 | +<section class="slide"> |
| 218 | + <h2>time calculations</h2> |
| 219 | + <p>datetime.timedelta([days[, seconds[, microseconds[, milliseconds[, minutes[, hours[, weeks]]]]]]]) represents a time difference |
| 220 | + <p>All the datetime objects overload the arithmetic operators, such that adding or subtracting dates will return a datetime.timedelta object |
| 221 | +<pre><code>In [17]: datetime.datetime.now() - datetime.datetime.now() |
| 222 | +Out[17]: datetime.timedelta(-1, 86399, 999987) |
| 223 | +</code></pre> |
| 224 | + <p>adding timedelta to a datetime will result in a new datetime |
| 225 | +<pre><code> |
| 226 | +In [19]: datetime.datetime(2019,11,1, tzinfo=pytz.UTC) + datetime.timedelta(days=1) |
| 227 | +Out[19]: datetime.datetime(2019, 11, 2, 0, 0) |
| 228 | + |
| 229 | +# or create a shortcut for creating a UTC timezone aware datetime: |
| 230 | +import functools |
| 231 | +utc = functools.partial(datetime.datetime, tzinfo=pytz.UTC) |
| 232 | +utc(2019,11,1,12,0) + datetime.timedelta(days=1) |
| 233 | +</code></pre> |
| 234 | + |
| 235 | +</section> |
| 236 | +<section class="slide"> |
| 237 | + <h2>time calculations</h2> |
| 238 | + <p>timedelta has a few functions and properties to retrieve the results: timedelta.total_seconds(), timedelta.days, timedelta.seconds, timedelta.microseconds |
| 239 | + <p>datetimes in the calculations must be all naive or all aware |
| 240 | +<pre><code> |
| 241 | +In [35]: datetime.datetime(2019,11,1,tzinfo=pytz.UTC) - datetime.datetime(2019,10,1) |
| 242 | +--------------------------------------------------------------------------- |
| 243 | +TypeError Traceback (most recent call last) |
| 244 | +<ipython-input-35-85c74b5e069b> in <module>() |
| 245 | +TypeError: can't subtract offset-naive and offset-aware datetimes |
| 246 | +</code><pre> |
| 247 | + |
| 248 | +</section> |
| 249 | + |
| 250 | +<section class="slide"> |
| 251 | + <h2>Storing datetimes</h2> |
| 252 | + <p>Try to deal in UTC as much as possible. Do not depend on the machine's interpretation of local time. |
| 253 | + <p>For instance, datetime.now() on my Mac will return a naive datetime containing the local time. Alternatively: |
| 254 | +<pre><code> |
| 255 | +# return the current time as an aware datetime in UTC: |
| 256 | +datetime.datetime.now(pytz.UTC) |
| 257 | +# this will return a naive datetime with the current UTC time |
| 258 | +datetime.datetime.utcnow() |
| 259 | +</code></pre> |
| 260 | + <p>When storing a datetime in a database, it will need to be translated into the database's native format. Depending on the database driver, it may or may not accept datetimes. If it does, it may or may not respect the timezone information |
| 261 | + <p>Discovering the behavior is part of your database integration work |
| 262 | +</section> |
| 263 | + |
| 264 | +<section class="slide"> |
| 265 | + <h2>datetimes in sqlite3</h2> |
| 266 | + <p>From the <a target="_blank" href="http://www.sqlite.org/datatype3.html">sqlite3 docs</a>: |
| 267 | +<pre> |
| 268 | +Dates and times in sqlite3 are stored as TEXT, REAL, or INTEGER values |
| 269 | + |
| 270 | +TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS"). |
| 271 | +REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar. |
| 272 | +INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC. |
| 273 | +</pre> |
| 274 | + <p>So Python datetime objects must be translated to these types |
| 275 | + <p>sqlite3 has support for "adapters" and "converters" to translate types going in and out of the DB |
| 276 | + <p><a target="_blank" href="https://docs.python.org/2/library/sqlite3.html#default-adapters-and-converters">Default adapters and converters</a> are supplied for date and datetime objects |
| 277 | + <p>unfortunately, the default implementation does not handle timezone aware datetimes, but they are simply implementable |
| 278 | + <p>let's look at |
| 279 | +examples/datetime_naive_to_sqlite.py |
| 280 | +and |
| 281 | +examples/datetime_aware_to_sqlite.py |
| 282 | +</section> |
| 283 | + |
| 284 | + |
| 285 | +<section class="slide"> |
| 286 | + <h2>The End</h2> |
| 287 | + <h2>Questions?</h2> |
| 288 | +</section> |
| 289 | + |
| 290 | + |
| 291 | + <!-- End slides. --> |
| 292 | + |
| 293 | + |
| 294 | + <!-- End slides. --> |
| 295 | + |
| 296 | + <!-- Begin extension snippets. Add or remove as needed. --> |
| 297 | + |
| 298 | + <!-- deck.navigation snippet --> |
| 299 | + <div aria-role="navigation"> |
| 300 | + <a href="#" class="deck-prev-link" title="Previous">←</a> |
| 301 | + <a href="#" class="deck-next-link" title="Next">→</a> |
| 302 | + </div> |
| 303 | + |
| 304 | + <!-- deck.status snippet --> |
| 305 | + <p class="deck-status" aria-role="status"> |
| 306 | + <span class="deck-status-current"></span> |
| 307 | + / |
| 308 | + <span class="deck-status-total"></span> |
| 309 | + </p> |
| 310 | + |
| 311 | + <!-- deck.goto snippet --> |
| 312 | + <!-- |
| 313 | + <form action="." method="get" class="goto-form"> |
| 314 | + <label for="goto-slide">Go to slide:</label> |
| 315 | + <input type="text" name="slidenum" id="goto-slide" list="goto-datalist"> |
| 316 | + <datalist id="goto-datalist"></datalist> |
| 317 | + <input type="submit" value="Go"> |
| 318 | + </form> |
| 319 | + --> |
| 320 | + <!-- End extension snippets. --> |
| 321 | + </div> |
| 322 | + |
| 323 | +<!-- Required JS files. --> |
| 324 | +<script src="deckjs/jquery.min.js"></script> |
| 325 | +<script src="deckjs/core/deck.core.js"></script> |
| 326 | + |
| 327 | +<!-- Extension JS files. Add or remove as needed. --> |
| 328 | +<script src="deckjs/extensions/menu/deck.menu.js"></script> |
| 329 | +<script src="deckjs/extensions/goto/deck.goto.js"></script> |
| 330 | +<script src="deckjs/extensions/status/deck.status.js"></script> |
| 331 | +<script src="deckjs/extensions/navigation/deck.navigation.js"></script> |
| 332 | +<script src="deckjs/extensions/scale/deck.scale.js"></script> |
| 333 | + |
| 334 | +<!-- Initialize the deck. You can put this in an external file if desired. --> |
| 335 | +<script> |
| 336 | + $(function() { |
| 337 | + $.deck('.slide'); |
| 338 | + }); |
| 339 | +</script> |
| 340 | +</body> |
| 341 | +</html> |
0 commit comments