Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Miroslav Šedivý - A Day Has Only 24±1 Hours: im...

Miroslav Šedivý - A Day Has Only 24±1 Hours: import pytz

Many people in Central Europe complain about two clock changes per year in March and in October that are known in advance and your system handles them without problems. As a developer, you should probably fear more all the clock changes you don’t know about…

After a short overview of the standard datetime module and its usage in different geographical contexts, we’ll have a look at the pytz library and discover all the 591 timezones it comes with. We’ll see why pytz is not a part of the standard library, as well as when and how and why this package gets frequent updates. This may be useful if your web app provides any timezones related functionality.

Background: I’ve spent most of my life in one time zone and travelled only a few times to another one. But I work daily with measurement data from sensors that are set to any random clock and have to interpret them correctly.

PyConWeb

July 17, 2018
Tweet

More Decks by PyConWeb

Other Decks in Programming

Transcript

  1. A Day Has Only 24±1 Hours: import pytz check the

    time don't check the time too often 4 / 67
  2. A Day Has Only 24±1 Hours: import pytz check the

    time don't check the time too often check what your government does 5 / 67
  3. A Day Has Only 24±1 Hours: import pytz check the

    time don't check the time too often check what your government does TIME ZONES! 6 / 67
  4. Miroslav Šedivý [ˈmɪrɔslaʋ ˈʃɛɟɪviː]   @eumiro born in Bratislava,

    Czechoslovakia (TZ=Europe/Bratislava) coded websites before they were cool M.Sc. at INSA Lyon, France (TZ=Europe/Paris) came to Python 2.5 from Perl/Java in 2008 Senior Software Architect at UBIMET GmbH in Karlsruhe, Germany (TZ=Europe/Berlin) using Python to make the sun shine and the wind blow 7 / 67
  5. >>> import time >>> time.time() 1530367380.0 >>> import datetime >>>

    datetime.datetime.now() datetime.datetime(2018, 6, 30, 16, 3, 0, 0) 10 / 67
  6. >>> import time >>> time.time() 1530367380.0 >>> import datetime >>>

    datetime.datetime.now() datetime.datetime(2018, 6, 30, 16, 3, 0, 0) datetime.datetime(2018, 6, 30, 14, 3, 0, 0) # on a server set to UTC 11 / 67
  7. >>> import time >>> time.time() 1530367380.0 >>> import datetime >>>

    datetime.datetime.now() datetime.datetime(2018, 6, 30, 16, 3, 0, 0) datetime.datetime(2018, 6, 30, 14, 3, 0, 0) # on a server set to UTC >>> datetime.datetime.utcnow() datetime.datetime(2018, 6, 30, 14, 3, 0, 0) 12 / 67
  8. >>> import time >>> time.time() 1530367380.0 >>> import datetime >>>

    datetime.datetime.now() datetime.datetime(2018, 6, 30, 16, 3, 0, 0) datetime.datetime(2018, 6, 30, 14, 3, 0, 0) # on a server set to UTC >>> datetime.datetime.utcnow() datetime.datetime(2018, 6, 30, 14, 3, 0, 0) >>> datetime.datetime.now(datetime.timezone.utc) datetime.datetime(2018, 6, 30, 14, 3, 0, 0, tzinfo=datetime.timezone.utc) 13 / 67
  9. >>> import time >>> time.time() 1530367380.0 >>> import datetime >>>

    datetime.datetime.now() datetime.datetime(2018, 6, 30, 16, 3, 0, 0) datetime.datetime(2018, 6, 30, 14, 3, 0, 0) # on a server set to UTC >>> datetime.datetime.utcnow() datetime.datetime(2018, 6, 30, 14, 3, 0, 0) >>> datetime.datetime.now(datetime.timezone.utc) datetime.datetime(2018, 6, 30, 14, 3, 0, 0, tzinfo=datetime.timezone.utc) >>> datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=2))) datetime.datetime(2018, 6, 30, 16, 3, 0, 0, tzinfo=datetime.timezone(datetime.timedelta(0, 7200))) 14 / 67
  10. >>> import time >>> time.time() 1530367380.0 >>> import datetime >>>

    datetime.datetime.now() datetime.datetime(2018, 6, 30, 16, 3, 0, 0) datetime.datetime(2018, 6, 30, 14, 3, 0, 0) # on a server set to UTC >>> datetime.datetime.utcnow() datetime.datetime(2018, 6, 30, 14, 3, 0, 0) >>> datetime.datetime.now(datetime.timezone.utc) datetime.datetime(2018, 6, 30, 14, 3, 0, 0, tzinfo=datetime.timezone.utc) >>> datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=2))) datetime.datetime(2018, 6, 30, 16, 3, 0, 0, tzinfo=datetime.timezone(datetime.timedelta(0, 7200))) >>> import pytz >>> datetime.datetime.now(pytz.timezone('Europe/Berlin')) datetime.datetime(2018, 6, 30, 16, 3, 0, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>) 15 / 67
  11. >>> today = datetime.datetime.utcnow().strftime('%Y-%m-%d') >>> yesterday = (datetime.datetime.utcnow() - datetime.timedelta(days=1)).strftime('%Y-%m-%d')

    >>> now = datetime.datetime.utcnow() >>> today = now.strftime('%Y-%m-%d') >>> yesterday = (now - datetime.timedelta(days=1)).strftime('%Y-%m-%d') 18 / 67
  12. >>> today = datetime.datetime.utcnow().strftime('%Y-%m-%d') >>> yesterday = (datetime.datetime.utcnow() - datetime.timedelta(days=1)).strftime('%Y-%m-%d')

    >>> now = datetime.datetime.utcnow() >>> today = now.strftime('%Y-%m-%d') >>> yesterday = (now - datetime.timedelta(days=1)).strftime('%Y-%m-%d') >>> start = datetime.datetime.utcnow() >>> expensive_operation() >>> end = datetime.datetime.utcnow() >>> elapsed = (end - start).total_seconds() 19 / 67
  13. >>> today = datetime.datetime.utcnow().strftime('%Y-%m-%d') >>> yesterday = (datetime.datetime.utcnow() - datetime.timedelta(days=1)).strftime('%Y-%m-%d')

    >>> now = datetime.datetime.utcnow() >>> today = now.strftime('%Y-%m-%d') >>> yesterday = (now - datetime.timedelta(days=1)).strftime('%Y-%m-%d') >>> start = datetime.datetime.utcnow() >>> expensive_operation() >>> end = datetime.datetime.utcnow() >>> elapsed = (end - start).total_seconds() >>> start = time.time() >>> expensive_operation() >>> end = time.time() >>> elapsed = end - start 20 / 67
  14. >>> today = datetime.datetime.utcnow().strftime('%Y-%m-%d') >>> yesterday = (datetime.datetime.utcnow() - datetime.timedelta(days=1)).strftime('%Y-%m-%d')

    >>> now = datetime.datetime.utcnow() >>> today = now.strftime('%Y-%m-%d') >>> yesterday = (now - datetime.timedelta(days=1)).strftime('%Y-%m-%d') >>> start = datetime.datetime.utcnow() >>> expensive_operation() >>> end = datetime.datetime.utcnow() >>> elapsed = (end - start).total_seconds() >>> start = time.time() >>> expensive_operation() >>> end = time.time() >>> elapsed = end - start >>> start = time.monotonic() >>> expensive_operation() >>> end = time.monotonic() >>> elapsed = end - start 21 / 67
  15. >>> today = datetime.datetime.utcnow().strftime('%Y-%m-%d') >>> yesterday = (datetime.datetime.utcnow() - datetime.timedelta(days=1)).strftime('%Y-%m-%d')

    >>> now = datetime.datetime.utcnow() >>> today = now.strftime('%Y-%m-%d') >>> yesterday = (now - datetime.timedelta(days=1)).strftime('%Y-%m-%d') >>> start = datetime.datetime.utcnow() >>> expensive_operation() >>> end = datetime.datetime.utcnow() >>> elapsed = (end - start).total_seconds() >>> start = time.time() >>> expensive_operation() >>> end = time.time() >>> elapsed = end - start >>> start = time.monotonic() >>> expensive_operation() >>> end = time.monotonic() >>> elapsed = end - start >>> time.monotonic_ns() # Python 3.7+ 22 / 67
  16. Time local solar time (equation of time) Greenwich France: 1891:

    l'heure de Paris (railroads at -00:05) 1911: Greenwich Time 25 / 67
  17. Time local solar time (equation of time) Greenwich France: 1891:

    l'heure de Paris (railroads at -00:05) 1911: Greenwich Time Greenwich and 25 time zones (-12 … +12) 26 / 67
  18. Time zones no central authority (countries, regions, counties, …) IANA.org

    (Internet Assigned Numbers Authority) → tzdata 28 / 67
  19. Time zones no central authority (countries, regions, counties, …) IANA.org

    (Internet Assigned Numbers Authority) → tzdata >>> len(pytz.all_timezones) 591 29 / 67
  20. Time zones no central authority (countries, regions, counties, …) IANA.org

    (Internet Assigned Numbers Authority) → tzdata >>> len(pytz.all_timezones) 591 >>> len(pytz.common_timezones) 439 30 / 67
  21. Time zones no central authority (countries, regions, counties, …) IANA.org

    (Internet Assigned Numbers Authority) → tzdata >>> len(pytz.all_timezones) 591 >>> len(pytz.common_timezones) 439 America (147), Asia (82), Europe (60), Africa (52), Australia (12), Antarctica (11) Pacific (38), Indian (11), Atlantic (10), Arctic (1) US (7), Canada (6) GMT, UTC 31 / 67
  22. >>> pytz.timezone('Europe/Berlin') <DstTzInfo 'Europe/Berlin' LMT+0:53:00 STD> >>> pytz.timezone('Europe/Paris') <DstTzInfo 'Europe/Paris'

    LMT+0:09:00 STD> >>> pytz.timezone('Europe/London') <DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD> 34 / 67
  23. >>> pytz.timezone('Europe/Berlin') <DstTzInfo 'Europe/Berlin' LMT+0:53:00 STD> >>> pytz.timezone('Europe/Paris') <DstTzInfo 'Europe/Paris'

    LMT+0:09:00 STD> >>> pytz.timezone('Europe/London') <DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD> >>> datetime.datetime(2018, 6, 30, 16, 3, tzinfo=pytz.timezone('Europe/Berlin')).astimezone(pytz.utc) datetime.datetime(2018, 6, 30, 15, 10, tzinfo=<UTC>) 35 / 67
  24. >>> pytz.timezone('Europe/Berlin') <DstTzInfo 'Europe/Berlin' LMT+0:53:00 STD> >>> pytz.timezone('Europe/Paris') <DstTzInfo 'Europe/Paris'

    LMT+0:09:00 STD> >>> pytz.timezone('Europe/London') <DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD> >>> datetime.datetime(2018, 6, 30, 16, 3, tzinfo=pytz.timezone('Europe/Berlin')).astimezone(pytz.utc) datetime.datetime(2018, 6, 30, 15, 10, tzinfo=<UTC>) >>> pytz.timezone('Europe/Berlin').localize(datetime.datetime(2018, 6, 30, 16, 3)).astimezone(pytz.utc) datetime.datetime(2018, 6, 30, 14, 3, tzinfo=<UTC>) 36 / 67
  25. IT Rome 1966-05-22 23:00:00 ES Madrid 1974-04-13 22:00:00 FR Paris

    1976-03-28 00:00:00 PL Warsaw 1977-04-03 00:00:00 NL Amsterdam 1977-04-03 01:00:00 BE Brussels 1977-04-03 01:00:00 CS Prague 1979-04-01 01:00:00 AT Vienna 1980-04-05 23:00:00 HU Budapest 1980-04-06 00:00:00 NO Oslo 1980-04-06 01:00:00 SE Stockholm 1980-04-06 01:00:00 DK Copenhagen 1980-04-06 01:00:00 DE Berlin 1980-04-06 01:00:00 CH Zurich 1981-03-29 01:00:00 (and DE Busingen) YU Belgrade 1983-03-27 01:00:00 37 / 67
  26. tzdata2018e.tar.gz (346k) 52800 Apr 23 01:52 africa 252 May 25

    2017 LICENSE 12934 Mar 14 23:49 antarctica 38969 Apr 30 21:46 Makefile 138358 Apr 30 21:28 asia 156727 May 2 08:44 NEWS 76124 Apr 1 18:27 australasia 150084 Mar 20 23:31 northamerica 4543 Jul 16 2017 backward 1182 Jun 20 2014 pacificnew 21945 Mar 18 22:48 backzone 2340 Jan 13 08:05 README 5567 Oct 2 2017 calendars 86505 Feb 21 00:50 southamerica 1008 Jun 2 2017 checklinks.awk 1538 Jun 16 2014 systemv 4203 Jan 30 18:16 checktab.awk 53216 Apr 15 20:02 theory.html 2895 Mar 2 18:43 CONTRIBUTING 6 May 2 08:44 version 2740 Aug 24 2016 etcetera 678 Jun 18 2014 yearistype.sh 168081 Apr 23 01:52 europe 2869 Apr 23 01:52 ziguard.awk 367 Aug 24 2016 factory 4207 Jan 23 23:54 zishrink.awk 4445 Feb 28 2017 iso3166.tab 17781 Feb 16 22:59 zone1970.tab 2196 Jan 15 18:19 leapseconds 1409 Aug 17 2014 zoneinfo2tdf.pl 2380 Jul 18 2017 leapseconds.awk 19165 Feb 16 18:21 zone.tab 10666 Jan 15 18:19 leap-seconds.list 38 / 67
  27. Europe/Berlin Zone Europe/Berlin 0:53:28 - LMT 1893 Apr 1:00 C-Eur

    CE%sT 1945 May 24 2:00 1:00 SovietZone CE%sT 1946 1:00 Germany CE%sT 1980 1:00 EU CE%sT 39 / 67
  28. Europe/Berlin Zone Europe/Berlin 0:53:28 - LMT 1893 Apr 1:00 C-Eur

    CE%sT 1945 May 24 2:00 1:00 SovietZone CE%sT 1946 1:00 Germany CE%sT 1980 1:00 EU CE%sT Rule EU 1977 1980 - Apr Sun>=1 1:00u 1:00 S Rule EU 1977 only - Sep lastSun 1:00u 0 - Rule EU 1978 only - Oct 1 1:00u 0 - Rule EU 1979 1995 - Sep lastSun 1:00u 0 - Rule EU 1981 max - Mar lastSun 1:00u 1:00 S Rule EU 1996 max - Oct lastSun 1:00u 0 - 40 / 67
  29. Europe/Istanbul Zone Europe/Istanbul 1:55:52 - LMT 1880 1:56:56 - IMT

    1910 Oct # Istanbul Mean Time? 2:00 Turkey EE%sT 1978 Oct 15 3:00 Turkey +03/+04 1985 Apr 20 2:00 Turkey EE%sT 2007 2:00 EU EE%sT 2011 Mar 27 1:00u 2:00 - EET 2011 Mar 28 1:00u 2:00 EU EE%sT 2014 Mar 30 1:00u 2:00 - EET 2014 Mar 31 1:00u 2:00 EU EE%sT 2015 Oct 25 1:00u 2:00 1:00 EEST 2015 Nov 8 1:00u 2:00 EU EE%sT 2016 Sep 7 3:00 - +03 41 / 67
  30. Europe/Istanbul Zone Europe/Istanbul 1:55:52 - LMT 1880 1:56:56 - IMT

    1910 Oct # Istanbul Mean Time? 2:00 Turkey EE%sT 1978 Oct 15 3:00 Turkey +03/+04 1985 Apr 20 2:00 Turkey EE%sT 2007 2:00 EU EE%sT 2011 Mar 27 1:00u 2:00 - EET 2011 Mar 28 1:00u 2:00 EU EE%sT 2014 Mar 30 1:00u 2:00 - EET 2014 Mar 31 1:00u 2:00 EU EE%sT 2015 Oct 25 1:00u 2:00 1:00 EEST 2015 Nov 8 1:00u 2:00 EU EE%sT 2016 Sep 7 3:00 - +03 (2011-03-10): […] Turkey will change into summer time zone (GMT+3) on March 28, 2011 at 3:00 a.m. instead of March 27. This change is due to a nationwide exam on 27th. [URL] Turkish: [URL] 42 / 67
  31. 2:00 EU EE%sT 2014 Mar 30 1:00u 2:00 - EET

    2014 Mar 31 1:00u […] (2014-02-14): The DST for Turkey has been changed for this year because of the Turkish Local election.... [URL] ... so Turkey will move clocks forward one hour on March 31 at 3:00 a.m. […] (2014-04-15): Having landed on a flight from the states to Istanbul (via AMS) on March 31, I can tell you that NOBODY (even the airlines) respected this timezone DST change delay. Maybe the word just didn't get out in time. […] (2014-06-15): The press reported massive confusion, as election officials obeyed the rule change but cell phones (and airline baggage systems) did not. See: [URL from 2014-03-30] I guess the best we can do is document the official time. 43 / 67
  32. 2:00 EU EE%sT 2015 Oct 25 1:00u 2:00 1:00 EEST

    2015 Nov 8 1:00u […] (2015-09-29): It's officially announced now by the Ministry of Energy. Turkey delays winter time to 8th of November 04:00 [URL] BBC News (2015-10-25): Confused Turks are asking "what's the time?" after automatic clocks defied a government decision ... "For the next two weeks #Turkey is on EEST... Erdogan Engineered Standard Time," said Twitter user @aysekarahasan. [URL] 44 / 67
  33. 2:00 EU EE%sT 2016 Sep 7 3:00 - +03 […]

    (2016-09-08): Turkey will stay in Daylight Saving Time even in winter.... [URL] […] (2016-09-07): The change is permanent, so this is the new standard time in Turkey. It takes effect today, which is not much notice. […] (2017-10-28): Turkey will go back to Daylight Saving Time starting 2018-10. [URL] […] (2017-11-08): ... today it was announced that the DST will become "continuous": [URL] […] (2017-11-08): Although Google Translate misfires on that source, it looks like Turkey reversed last month's decision, and so will stay at +03. 45 / 67
  34. America/Caracas Zone America/Caracas -4:27:44 - LMT 1890 -4:27:40 - CMT

    1912 Feb 12 # Caracas Mean Time? -4:30 - -0430 1965 Jan 1 0:00 -4:00 - -04 2007 Dec 9 3:00 -4:30 - -0430 2016 May 1 2:30 -4:00 - -04 […] (2016-04-15): Clocks advance 30 minutes on 2016-05-01 at 02:30.... […] [URL from Reuters] […] (2016-04-20): ... published in the official Gazette [2016-04-18], here: [URL from .ve] 46 / 67
  35. America/Port-au-Prince Rule Haiti 2005 2006 - Apr Sun>=1 0:00 1:00

    D Rule Haiti 2005 2006 - Oct lastSun 0:00 0 S Rule Haiti 2012 2015 - Mar Sun>=8 2:00 1:00 D Rule Haiti 2012 2015 - Nov Sun>=1 2:00 0 S Rule Haiti 2017 max - Mar Sun>=8 2:00 1:00 D Rule Haiti 2017 max - Nov Sun>=1 2:00 0 S […] (2005-04-15) […] wrote me that Haiti is now on DST. I searched for confirmation, and I found a press release on the Web page of the Haitian Consulate in Chicago (2005-03-31), […] […] (2006-04-04) I have been informed by users that Haiti observes DST this year like last year […] […] (2012-03-11) According to several news sources, Haiti will observe DST this year, apparently using the same start and end date as USA/Canada. […] […] (2013-03-10) It appears that Haiti is observing DST this year as well, same rules as US/Canada. They did it last year as well, and it looks like they are going to observe DST every year now... […] […] (2016-03-12) […] informed us that Haiti are not going on DST this year. […] […] (2017-03-12) We have received 4 mails from different people telling that Haiti has started DST again today, and this source seems to confirm that, I have not been able to find a more authoritative source: [URL] 47 / 67
  36. Asia/Pyongyang […] (2015-08-07) According to many news sources, North Korea

    is going to change to the 8:30 time zone on August 15 […] (2015-08-15) Bells rang out midnight (00:00) Friday as part of the celebrations. […] […] (2018-04-29) North Korea will revert its time zone from UTC+8:30 (PYT; Pyongyang Time) back to UTC+9 (KST; Korea Standard Time). […] (2018-04-30) […] It appears to be the front page story at the top in the right-most column. 48 / 67
  37. Asia/Pyongyang >>> datetime.datetime.now(pytz.timezone('Asia/Pyongyang')) datetime.datetime(2018, 6, 29, 22, 33, 0, tzinfo=<DstTzInfo

    'Asia/Pyongyang' KST+8:30:00 STD>) $ TZ='Asia/Pyongyang' date Fri Jun 29 23:03:00 KST 2018 50 / 67
  38. Asia/Pyongyang >>> datetime.datetime.now(pytz.timezone('Asia/Pyongyang')) datetime.datetime(2018, 6, 29, 22, 33, 0, tzinfo=<DstTzInfo

    'Asia/Pyongyang' KST+8:30:00 STD>) $ TZ='Asia/Pyongyang' date Fri Jun 29 23:03:00 KST 2018 $ pacman -Qs tz community/python-pytz 2018.4-1 Cross platform time zone library for Python core/tzdata 2018e-1 Sources for time zone and daylight saving time data 51 / 67
  39. Asia/Pyongyang >>> datetime.datetime.now(pytz.timezone('Asia/Pyongyang')) datetime.datetime(2018, 6, 29, 22, 33, 0, tzinfo=<DstTzInfo

    'Asia/Pyongyang' KST+8:30:00 STD>) $ TZ='Asia/Pyongyang' date Fri Jun 29 23:03:00 KST 2018 $ pacman -Qs tz community/python-pytz 2018.4-1 Cross platform time zone library for Python core/tzdata 2018e-1 Sources for time zone and daylight saving time data Android 7: 2016f 52 / 67
  40. Reading data >>> datetime.datetime.strptime('30.06.2018 16:03 +0200', '%d.%m.%Y %H:%M %z') datetime.datetime(2018,

    6, 30, 16, 3, tzinfo=datetime.timezone(datetime.timedelta(0, 7200))) 53 / 67
  41. Reading data >>> datetime.datetime.strptime('30.06.2018 16:03 +0200', '%d.%m.%Y %H:%M %z') datetime.datetime(2018,

    6, 30, 16, 3, tzinfo=datetime.timezone(datetime.timedelta(0, 7200))) >>> from dateutil import parser, tz >>> tzinfos = {'MUE': tz.gettz('Europe/Berlin')} >>> parser.parse('30.06.2018 16:03 MUE', tzinfos=tzinfos) datetime.datetime(2018, 6, 30, 16, 3, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')) 54 / 67
  42. Reading data >>> datetime.datetime.strptime('30.06.2018 16:03 +0200', '%d.%m.%Y %H:%M %z') datetime.datetime(2018,

    6, 30, 16, 3, tzinfo=datetime.timezone(datetime.timedelta(0, 7200))) >>> from dateutil import parser, tz >>> tzinfos = {'MUE': tz.gettz('Europe/Berlin')} >>> parser.parse('30.06.2018 16:03 MUE', tzinfos=tzinfos) datetime.datetime(2018, 6, 30, 16, 3, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')) Data without timezone info? check end of March/October for missing/repeating entries 55 / 67
  43. Reading data >>> datetime.datetime.strptime('30.06.2018 16:03 +0200', '%d.%m.%Y %H:%M %z') datetime.datetime(2018,

    6, 30, 16, 3, tzinfo=datetime.timezone(datetime.timedelta(0, 7200))) >>> from dateutil import parser, tz >>> tzinfos = {'MUE': tz.gettz('Europe/Berlin')} >>> parser.parse('30.06.2018 16:03 MUE', tzinfos=tzinfos) datetime.datetime(2018, 6, 30, 16, 3, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')) Data without timezone info? check end of March/October for missing/repeating entries if you have some solar data, match sunrise/sunset 56 / 67
  44. Arrow and other convenient libs PostgreSQL: TIMESTAMP WITH TIME ZONE

    / TIMESTAMPTZ stored as UTC, converts in SQL SELECT colname AT TIME ZONE 'Europe/Berlin' FROM … 58 / 67
  45. Best practices don't invent your own time zones don't hard

    code any rules keep your time zone libs up-to-date 61 / 67
  46. Best practices don't invent your own time zones don't hard

    code any rules keep your time zone libs up-to-date convert from local time to UTC as soon as possible (be precise or guess) 62 / 67
  47. Best practices don't invent your own time zones don't hard

    code any rules keep your time zone libs up-to-date convert from local time to UTC as soon as possible (be precise or guess) convert from UTC to local time as late as possible 63 / 67
  48. Best practices don't invent your own time zones don't hard

    code any rules keep your time zone libs up-to-date convert from local time to UTC as soon as possible (be precise or guess) convert from UTC to local time as late as possible follow your government's intentions to modify your time zone and inform [email protected] 64 / 67
  49. Best practices don't invent your own time zones don't hard

    code any rules keep your time zone libs up-to-date convert from local time to UTC as soon as possible (be precise or guess) convert from UTC to local time as late as possible follow your government's intentions to modify your time zone and inform [email protected] store recurrent events “every day at 10:00 local time” in original form and convert them to datetimes 65 / 67
  50. Best practices don't invent your own time zones don't hard

    code any rules keep your time zone libs up-to-date convert from local time to UTC as soon as possible (be precise or guess) convert from UTC to local time as late as possible follow your government's intentions to modify your time zone and inform [email protected] store recurrent events “every day at 10:00 local time” in original form and convert them to datetimes AoE (Anywhere on Earth) “2018-06-30 AoE” 2018-06-30 23:59:59 Pacific/Samoa 2018-07-01 10:59:59 UTC 2018-07-01 12:59:59 CEST (11:59:59 in winter) 66 / 67
  51. “The enjoyment of one's tools is an essential ingredient of

    successful work.” Donald E. Knuth Miroslav Šedivý [ˈmɪrɔslaʋ ˈʃɛɟɪviː]  eumiro  eumiro  šedivý 67 / 67