<story><title>What the Royal Astronomical Society in 1884 Tells Us About Python Today</title><url>https://typesandtimes.net/2019/05/royal-astronomical-society-python</url></story><parent_chain></parent_chain><comment><author>svat</author><text>It took me some effort to understand the issue here, so an alternative explanation in case it helps someone.&lt;p&gt;First, the part that&amp;#x27;s independent of programming language. You may want to read about absolute time and civil time (e.g. from &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;abseil.io&amp;#x2F;docs&amp;#x2F;cpp&amp;#x2F;guides&amp;#x2F;time&quot; rel=&quot;nofollow&quot;&gt;https:&amp;#x2F;&amp;#x2F;abseil.io&amp;#x2F;docs&amp;#x2F;cpp&amp;#x2F;guides&amp;#x2F;time&lt;/a&gt;) but if you don&amp;#x27;t, in short: “civil time” refers to something like “2019 May 26 at 2:45 pm in New York City” (or “in the America&amp;#x2F;New_York time zone”), which means (roughly) whatever time the locals in New York City (or a larger shared geo-political zone) would agree is 2:45 pm on that date. To convert this to an absolute time, or in other words to make sense of “2019-05-26 14:45 in America&amp;#x2F;New_York”, we need data about the real world &lt;i&gt;as of that date&lt;/i&gt;: most obviously we need to know whether Daylight-Saving Time was in effect on that date, but also what conventions were in use at the time. (This also means it&amp;#x27;s hard to know for certain what such a notation in the future means in terms of absolute time, as possibly DST could be abolished or the dates when it comes into effect could change.)&lt;p&gt;It so happens that in 1884 the conventions of New York City were such that it was about 4 minutes ahead of the then-recently standardized Eastern Time, so about 4 hours and 56 behind GMT.&lt;p&gt;So, in any “correct” library, we should see the following respected:&lt;p&gt;• “2019 May 26 at 2:45 pm in New York” should mean “2019 May 26 at 18:45 UTC” (timezone is EDT i.e. UTC minus 4 hours).&lt;p&gt;• “2019 Jan 26 at 2:45 pm in New York” should mean “2019 Jan 26 at 19:45 UTC” (timezone is EST, i.e. UTC minus 5 hours).&lt;p&gt;• “1884 Jan 26 at 2:45 pm in New York” should mean “1884 Jan 26 at 19:41 UTC” (timezone is... GMT minus 4 hours and 56 minutes).&lt;p&gt;----&lt;p&gt;Now the part that&amp;#x27;s Python-specific: the pytz library in Python provides two ways of constructing such a well-formed civil time. One is to call `.localize` on a timezone, and the other is to call `.astimezone` to convert from one civil time to its equivalent (the same absolute time) in another timezone, thus obtaining a new civil time. Both are illustrated below, showing it working properly:&lt;p&gt;&lt;pre&gt;&lt;code&gt;    &amp;gt;&amp;gt;&amp;gt; pytz.timezone(&amp;#x27;America&amp;#x2F;New_York&amp;#x27;).localize(datetime.datetime(2019, 5, 26, 14, 45, 0)).astimezone(pytz.utc)
    datetime.datetime(2019, 5, 26, 18, 45, tzinfo=&amp;lt;UTC&amp;gt;)
    
    &amp;gt;&amp;gt;&amp;gt; pytz.timezone(&amp;#x27;America&amp;#x2F;New_York&amp;#x27;).localize(datetime.datetime(2019, 1, 26, 14, 45, 0)).astimezone(pytz.utc)
    datetime.datetime(2019, 1, 26, 19, 45, tzinfo=&amp;lt;UTC&amp;gt;)
    
    &amp;gt;&amp;gt;&amp;gt; pytz.timezone(&amp;#x27;America&amp;#x2F;New_York&amp;#x27;).localize(datetime.datetime(1884, 1, 26, 14, 45, 0)).astimezone(pytz.utc)
    datetime.datetime(1884, 1, 26, 19, 41, tzinfo=&amp;lt;UTC&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
Unfortunately, there&amp;#x27;s a third thing a programmer can do, which the documentation warns against (&lt;a href=&quot;http:&amp;#x2F;&amp;#x2F;pytz.sourceforge.net&amp;#x2F;#localized-times-and-date-arithmetic&quot; rel=&quot;nofollow&quot;&gt;http:&amp;#x2F;&amp;#x2F;pytz.sourceforge.net&amp;#x2F;#localized-times-and-date-arithm...&lt;/a&gt;), and that is to pass one of pytz&amp;#x27;s timezone objects as the “tzinfo” parameter to the standard library `datetime` function:&lt;p&gt;&lt;pre&gt;&lt;code&gt;    &amp;gt;&amp;gt;&amp;gt; datetime.datetime(2019, 5, 26, 14, 45, 0, tzinfo=pytz.timezone(&amp;#x27;America&amp;#x2F;New_York&amp;#x27;)).astimezone(pytz.utc) # Don&amp;#x27;t do this!
    datetime.datetime(2019, 5, 26, 19, 41, tzinfo=&amp;lt;UTC&amp;gt;)
    &amp;gt;&amp;gt;&amp;gt; datetime.datetime(2019, 1, 26, 14, 45, 0, tzinfo=pytz.timezone(&amp;#x27;America&amp;#x2F;New_York&amp;#x27;)).astimezone(pytz.utc) # Don&amp;#x27;t do this!
    datetime.datetime(2019, 1, 26, 19, 41, tzinfo=&amp;lt;UTC&amp;gt;)
    &amp;gt;&amp;gt;&amp;gt; datetime.datetime(1884, 1, 26, 14, 45, 0, tzinfo=pytz.timezone(&amp;#x27;America&amp;#x2F;New_York&amp;#x27;)).astimezone(pytz.utc) # Don&amp;#x27;t do this!
    datetime.datetime(1884, 1, 26, 19, 41, tzinfo=&amp;lt;UTC&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;
which is certainly consistent in its own way, but only the last one is correct. Oops.&lt;p&gt;The issue here is in the interaction between the “tzinfo” model of the standard-library `datetime` and pytz&amp;#x27;s timezone objects: the result is that when the two are used together in the above incorrect way, one ends up with a timezone that is a fixed offset from UTC, which is silly. A timezone like `America&amp;#x2F;New_York` is &lt;i&gt;not&lt;/i&gt; a fixed offset from UTC: not only does it change twice a year, it also has changed in arbitrary ways in the past, and may change in arbitrary ways in the future.&lt;p&gt;(Note that “fixing” the offset of 4 hour 56 minutes to 5 hours would not solve any problems as it would still be wrong many months of each year — arguably, having an obviously incorrect result may even be better than a sometimes-correct one.)&lt;p&gt;The linked blog post by Paul Ganssle (&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;blog.ganssle.io&amp;#x2F;articles&amp;#x2F;2018&amp;#x2F;03&amp;#x2F;pytz-fastest-footgun.html&quot; rel=&quot;nofollow&quot;&gt;https:&amp;#x2F;&amp;#x2F;blog.ganssle.io&amp;#x2F;articles&amp;#x2F;2018&amp;#x2F;03&amp;#x2F;pytz-fastest-footgu...&lt;/a&gt;), the author of the `dateutil` (not to be confused with the standard-library `datetime`) library, is also informative.</text></comment>
<story><title>What the Royal Astronomical Society in 1884 Tells Us About Python Today</title><url>https://typesandtimes.net/2019/05/royal-astronomical-society-python</url></story><parent_chain></parent_chain><comment><author>bryanrasmussen</author><text>Ok, I don&amp;#x27;t understand though why the bug hasn&amp;#x27;t been fixed and is there any other widely used time localization library that makes the same mistake - not just in python but other languages?</text></comment>