
2020 Leap Day Bugs - mj1586
https://codeofmatt.com/list-of-2020-leap-day-bugs/
======
rachelbythebay
Let's not forget the stuff that'll break December 31, 2020... because it'll be
day 366 (or 365, if 0-indexed).

Anyone still using a Zune out there?

~~~
kclay
I have an old Zune. Going give this a try this year

~~~
swypych
For the lazy
[https://www.theguardian.com/technology/blog/2009/jan/01/zune...](https://www.theguardian.com/technology/blog/2009/jan/01/zune-
firmware-mistake)

~~~
henriquemaia
Well... thanks. But not because I'm lazy, for I had no idea about this bug
there. I just thought OP had an off topic moment based on random connection.

But now, after reading your link and giving it a bit of thought, what s/he
wrote makes perfect sense _even without your link_.

So... thanks.

------
hmate9
This is why no one should ever ever write their own Time or Date library. The
number of edge cases is simply enormous

~~~
Misdicorl
Almost all the edge cases with time have to do with localization.

Build your time library on (un)signed 64 bit integers representing the number
of nanoseconds since the utc epoch. Adjust above sentence to reflect the level
of precision and range your use case needs. You are now done for 80% of use
cases (perf timing, logging, timeouts, event storage, event ordering within
jitter).

If you need to parse/display for humans or have something happen at a
particular time in a particular timezone, things get gross. But that's no
different than any other situation where you eventually have to interface
machine data with humans. Either it's your particular expertise or it's a
distraction and you should use someone else's solution.

~~~
earthboundkid
> Build your time library on (un)signed 64 bit integers representing the
> number of nanoseconds since the utc epoch.

You poor sweet summer child. Has no one told you about the leap seconds yet?
Unix time is _not_ the number of seconds since epoch. It deliberately excludes
leap seconds, which happen unpredictably whenever scientists measure the Earth
as having spun at a different enough speed for long enough.

Time is fucked on every level:

\- Philosophical: What is time? We just don't know.

\- Physical: Turns out there is no such thing as simultaneity, and time flows
differently at different locations. Time may be discrete at the Planck level,
but we don't really know yet.

\- Cosmological: The Earth does not rotate at a constant speed, the Earth does
not orbit the Sun at a fractional component of its rotation, and the Moon does
not orbit at even ratio either.

\- Historical: Humans have not used time or calendars consistently.

\- Notational: Some time notations are ambiguous (e.g. during daylight savings
transitions) and others are skipped.

\- Regional: Different regions use subtly different clocks and calendars.

\- Political: Different political actors choose to change time whenever they
feel like it with little or no warning.

\- Religious: Many religions come with their own system for timekeeping, and
people don't like when outsiders impose other systems.

~~~
perl4ever
"You poor sweet summer child"

haha. Google is way ahead of you and your "leap seconds".

"Since 2008, instead of applying leap seconds to our servers using clock
steps, we have "smeared" the extra second across the hours before and after
each leap. The leap smear applies to all Google services, including all our
APIs."

[https://developers.google.com/time/smear](https://developers.google.com/time/smear)

...now write code to convert Google time to any other random type of time.

------
tzs
Sears appears to have have taken this to a new level. I got two emails from
them today. The first arrived at 6:36 AM, with subject

> Confirmed! Your mystery Leap Year offer awaits inside. How much will you
> save?

The second arrived at 9:54 AM, with subject

> Oops! Your code is fixed. (We shoulda looked before we leap-yeared...)

The messages themselves appear to be identical except for some query
parameters on some URLs, so I'm guessing that whatever they botched for leap
year was on the server when one tried to respond to the offer.

Botching Feb 29 in general date handling code is embarrassing, but there is it
least a somewhat plausible excuse that you just forgot about that special
case. But botching Feb 29 in code that is meant to only work on Feb 29? Wow.

~~~
scarmig
Botching is pretty ungenerous. All of the above can easily happen in the
context of a complicated software system.

Experienced people who avoid these issues are mostly experienced with smashing
headfirst into one of these tricky areas and approaching them with an attitude
of "here be dragons" afterwards. One bitten, twice shy.

------
xxpor
I haven't been able to figure out if this is a Google issue or King County
Metro issue yet, but google maps transit directions are completely broken
today. Every suggestion is "take Lyft" or wait until 4 am tomorrow.

~~~
indecisive_user
Interesting observation.

I played around with it a little on google maps. It says it pulls route
information directly from the King County transit website[1] which lists
routes by the day of the week, not by the date, and seems to be displaying the
routes for today just fine. My guess is that it's a Google issue, but maybe
the King County transit website was broken earlier today and gmaps is just
serving the cached routes now.

For anyone curious, this is what it currently looks like to get from Bellevue
to Redmond in google maps.
[https://i.imgur.com/wMTIzBL.png](https://i.imgur.com/wMTIzBL.png)

[1][https://kingcounty.gov/depts/transportation/metro/schedules-...](https://kingcounty.gov/depts/transportation/metro/schedules-
maps/route/b-line.aspx)

------
JensenDied
I ran into this already on January first. Someone in the last 4 years
changed/created a function in a MUD that I now maintain, to convert Unix
timestamps into iso8601 strings for MySQL.

They did the math for which year is a leap year incorrectly, which wasn't the
bug that bit us, they helpfully added the leap day regardless of where in the
year it was. The tests they wrote targeted the middle of the year and missed
it. I just pulled in date2j and related match from postgres, added more test
cases.

------
dictum
Random thought: we're now closer to 2038 than to Y2K.

~~~
martin-adams
and only heading in that direction

~~~
gumby
Speak for yourself.

------
user982
A line in one of my Python daemons has been crashing it repeatedly all day:

    
    
      isotime = datetime.strptime(time.get('title'), '%a %d %b %I:%M:%S %p').replace(year=YEAR, tzinfo=TZ).isoformat()
        ValueError: day is out of range for month
    

It's not mission-critical, so I'm just going to wait it out until tomorrow.

EDIT: Hacked it out.

    
    
      isotime = datetime.strptime(f"{time.get('title')} {YEAR} {tzoffset:+03d}00", '%a %d %b %I:%M:%S %p %Y %z').isoformat()

~~~
phoobahr
That strap time parsing doesn't fail under python 2.7.17 or 3.8.1

~~~
user982

      Python 2.7.17 (default, Dec 31 2019, 23:59:25) 
      Type "help", "copyright", "credits" or "license" for more information.
      >>> from datetime import datetime
      >>> datetime.strptime('Sat 29 Feb 12:00:00 PM', '%a %d %b %I:%M:%S %p')
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      ValueError: day is out of range for month

~~~
park_94110
This is because 1900 is the default year in datetime. 1900 was not a leap
year.

~~~
user982
I know, I'm just providing a counterexample to phoobahr.

------
btmiller
LinkedIn has one! Yesterday it listed my time with my current employer as 2
years and 9 months. Today, it’s 2 years and 8 months! Can anyone venture a
guess on how I time traveled?

~~~
jobigoud
Happy -1st of February.

------
smallstepforman
So frustrating that my embedded code works without a hitch and our company is
nearly bankrupt, while our bank with billions of dollars in annual profit goes
offline since it cant handle leap years.

------
davedunkin
My bank, IberiaBank, deposited interest and sent out statements today, with
them being dated March 1.

------
ddevault
Here's mine:

[https://git.sr.ht/~sircmpwn/meta.sr.ht/commit/e0be9dcd8e96f9...](https://git.sr.ht/~sircmpwn/meta.sr.ht/commit/e0be9dcd8e96f952547c243e078c47571f54fca2)

~~~
kgabbott
I respect wanting or needing to do this with just the builtin datetime module.
For everyone else, I recommend relativedelta from the dateutil package:
[https://dateutil.readthedocs.io/en/stable/relativedelta.html](https://dateutil.readthedocs.io/en/stable/relativedelta.html)

e.g.:

    
    
      >>> from dateutil.relativedelta import relativedelta
      >>> date = datetime.utcnow().date()
      >>> date
      datetime.date(2020, 2, 29)
      >>> date + relativedelta(years=1)
      datetime.date(2021, 2, 28)

~~~
mj1586
I added to here:
[https://stackoverflow.com/a/60468711/634824](https://stackoverflow.com/a/60468711/634824)

Thanks.

------
andreygrehov
So happy to see Timehop in this list. I was a little worried about releasing
not-a-bug-but-a-feature, but it played out great :)

~~~
mj1586
I love the image, BTW! :)

------
mercora
huh! i just noticed few hours ago a pi i use has had the wrong date which was
set to a day earlier. I quickly determined a faulty ntp server i had hard
configured was giving the bogus time. In case it is of interest its address
was "144.76.60.190" and was chosen by random chance because i cant resolve
names without working time on that machine (don't ask). It appears to be taken
offline by now though but i checked its giving the wrong time before changing
to another server.

Also, but probably unrelated, my WiFi stopped working exactly 00:00AM CET but
that might be unrelated as it does that all the time :)

------
Yessing
is there something special about 2020 (regarding leap years)?

the rule I know is divisible by 4 && ( not divisible by 100 || divisible by
400) so 2020 is not even an edge case.

~~~
HocusLocus
I think the answer you were looking for was, apparently NO there is nothing
special about 2020 in relation to this problem. Despite the misleading use of
year 2020 in the title and the title of the linked page, what is being
experienced is a simple mis-coding of year+1 calculations. The title could
have been,

"Leap day bugs, again! People, must we go through this every 4 years?"

So this is a crop of bugs that are either code written in the last 4 years or
untested/unnoticed/unreported 4 years ago.

One of the Sprint examples (someone roaming between two cell towers and their
date changing back and forth) is especially disturbing.

~~~
blowski
Or there's an unfinished ticket in the backlog that says "Oh gosh, let's just
bodge all these values in the database and make sure we fix it by the next
leap year".

------
ginko
I'm kinda surprized that date libraries break with a simple leap year like
2020. I'd have more understanding if it happened with the year 2000(leap year)
or 2100(not a leap year), but the basic "is year divisible by 4" check should
be good enough until 2100.

------
jeromebaek
relevant: falsehoods programmers believe about time
[https://news.ycombinator.com/item?id=4128208](https://news.ycombinator.com/item?id=4128208)

------
imroot
LogRocket is reporting every date as Feb 28, 2020.

------
imperialdrive
The bulk of our deployed Yealink T56 phones displayed February calendar events
for the wrong day. I never did find out why.

~~~
pm215
I hear this is a firmware bug, and should automatically sort itself out once
we hit March (but there is a bugfixed f/w due too). My pet theory/guess was
that there was a misimplementation of some algorithm like Zeller's congruence
([https://en.m.wikipedia.org/wiki/Zeller%27s_congruence](https://en.m.wikipedia.org/wiki/Zeller%27s_congruence))
for calculating the day of the week, which works on an adjusted year starting
in March, because I couldn't come up with any other reason why a leap year bug
would manifest for all days before feb 29 and then not thereafter. But there
is probably a duller explanation :-)

------
hipjiveguy
I deposited a cheque using the ScotiaBank mobile app on March 1, and when I
looked at it on my online banking page, it said that it was deposited on March
2!

Seems hard to believe that a mobile app would be allowed to tell the server
what time the cheque was deposited!

------
anonsivalley652
Stupid Q:

Why doesn't the syslog protocol (RFC5424) deal with leap seconds (the seconds
field goes to 00-59, not 00-60)? Are they using UTC (they would have to ignore
LS and have crappier logs) or TAI (doesn't have LS)?

[https://mailarchive.ietf.org/arch/msg/syslog/DDLgKsRPITFXYSB...](https://mailarchive.ietf.org/arch/msg/syslog/DDLgKsRPITFXYSBzicpbqTwtbk8/)

[http://www.madore.org/~david/computers/unix-leap-
seconds.htm...](http://www.madore.org/~david/computers/unix-leap-seconds.html)

[https://tools.ietf.org/html/rfc5424](https://tools.ietf.org/html/rfc5424)

[https://cr.yp.to/libtai/tai64.html](https://cr.yp.to/libtai/tai64.html)

[https://cr.yp.to/libtai.html](https://cr.yp.to/libtai.html)

~~~
marcosdumay
Unix systems usually do not have extra seconds after 23:59:59. Leap seconds
are normally dealt with by slowing your clock down. So Unixes use neither UTC
or TAI, they use something else.

It's something that does not matter in 99.99% of the cases, and that other
00.01% need specialized hardware and software for dealing with it anyway.

~~~
wahern
More specifically, POSIX _explicitly_ defines a day as having precisely 86400
seconds. This makes calendar arithmetic trivial, including into the future,
which would otherwise be impossible as leap seconds aren't predictable. Any
program with a leap day bug is almost certainly not making use of Unix
timestamps.

The corollary is that for the most commonly used APIs a Unix second isn't the
same thing as an SI second. A Unix second is effectively defined in terms of
the civil calendar, not as a fixed quantum of physical time.

Technically this doesn't preclude Unix date-time strings from displaying a
60th second. (And maybe some do.) But it would require unnecessary extra work,
introduce inconsistencies (i.e. a generated string for a future date-time that
happened to be a leap second would show :59 today but :60 at the moment of the
leap second), and invite bugs.

------
geforce
My car clock went back from 2:00AM to 12:00. I was really wondering what
happened.

------
est31
Just solve it the Google way: [https://xkcd.com/2266/](https://xkcd.com/2266/)

------
xmdx
I think I found one. My Amex bill is due on the 29th and I have a direct debit
to pay it automatically. However it never got taken. First time this has ever
happened.

Let's see if it happens today since it's now the 1st.

------
system2
Considering billions of devices and services not being affected, these bugs
are very insignificant. At least we figured out leap day bugs and not having
y2k crisis' every four years.

------
tech234a
My Timex Expedition watch also skipped to March 1.

~~~
makomk
That's fairly normal for older or more basic digital watches - they don't know
anything about the year, so every four years you have to manually set them
back the 29th of February. I think that people see this as a bug says
something about changing expectations. Pre-digital watches generally didn't
know about months either, so required manual date adjustment every other
month. The framing of this manual adjustment as a bug feels like it comes from
our experience of dealing with computer software all the time.

~~~
schoen
Or from dealing with more recent digital watches that do know about the year.

------
lamby
Does anyone remember the fail2ban issue where it used 100% CPU for the entire
day?

------
susam
I learnt the following test for leap year early in my software engineering
career from the book "The C Programming Language" by Kernighan & Ritchie (see
section 2.5 Arithmetic Operators):

    
    
      (year % 4 == 0 && year % 100 != 0) || year % 400 == 0
    

The following test also works:

    
    
      year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)

~~~
thaumasiotes
For those who were curious why these could both work:

(A & B) | C differs from A & (B | C) in two cases: when A is false, C is true,
and B is any value.

But in this case in particular, those differences would create a bug when A is
false (the year is not divisible by 4) at the same time that C is true (the
year _is_ divisible by 400). Since that can't happen, the bug cannot occur.

