Chris's Wiki :: blog/sysadmin/ServerUTCTimeViews Commentshttps://utcc.utoronto.ca/~cks/space/blog/sysadmin/ServerUTCTimeViews?atomcommentsDWiki2020-06-12T05:12:17ZRecent comments in Chris's Wiki :: blog/sysadmin/ServerUTCTimeViews.By Andrew on /blog/sysadmin/ServerUTCTimeViewstag:CSpace:blog/sysadmin/ServerUTCTimeViews:c5b5d966a5b0c679bf09b43dcdff4c03f2802ccdAndrew<div class="wikitext"><p>The worst bug I ever encountered that was caused by local timezones is as follows:</p>
<p>This company operated a web-based marketplace, with buyers and sellers, and the company taking a cut of every sale. They also had a "referral program", where sellers could recruit other sellers, and the seller that did the recruiting would get a small cut of every sale their recruits made for the first two years.</p>
<p>This was implemented with a check in the purchase flow that looked something like</p>
<pre>
if datetime.now().subtract(2 years) < seller.signup_date && seller.referrer {
// give the referrer their cut
}
</pre>
<p>and then, one day in march, in the middle of the night, every single purchase failed for one hour.</p>
<p>Why? Because it was between 1am and 2am local time, on whichever day in March happened to be the date of the DST switch two years earlier. And when <code>datetime.now().subtract(2 years</code>) tried to produce a time of, say, "1:03am, March 14th 2010, America/New_York", it threw an "invalid datetime" exception, because there <em>was</em> no 1:03am on that date.</p>
<p>Some people might consider this a bug, but the author of the datetime library sensibly argued that it was correct behavior. One way to guarantee that the result of time interval arithmetic exists is to use UTC; another way is to subtract an interval that consists of a number of <em>seconds</em>, instead of slippery units like months and years (some libraries, like Go 'time', <em>only</em> support these concrete intervals). But if you ask for "two years ago, on the same day, at the same time", you might just be asking for a time that doesn't exist.</p>
<p>Eagle-eyed readers will notice that <em>all</em> purchases failed because we didn't order the operations to take advantage of short-circuiting — the datetime math was done even for accounts that were never part of the referrer program. This was considered to have no impact on performance, but it turns out that performance isn't the only thing.</p>
<p>If we had done something like</p>
<pre>
if datetime.now() < seller.signup_date.add(2 years) && seller.referrer
</pre>
<p>which <em>looks</em> like it should be mathematically equivalent, the result would have been even stranger... purchases would have failed every single time, regardless of the time the purchase was made, but only for those sellers unlucky enough to sign up in the hour that would be missing to the "spring forward" two years later.</p>
</div>2020-06-12T05:12:17Z