Hacker News new | past | comments | ask | show | jobs | submit login
2020 Leap Day Bugs (codeofmatt.com)
312 points by mj1586 on Feb 29, 2020 | hide | past | favorite | 146 comments



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?


Zune HD user here (albeit it's been sitting on my desk lately...)

I'm definitely going to try it


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



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.


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


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.


> 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.


"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

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


Both

https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd...

and https://en.wikipedia.org/wiki/Unix_time

indicate that leap seconds are not included in Unix time as seconds-since-the-epoch. Leap seconds are included in UTC, and thus the Unix time appears to skip a second relative to UTC.


> Unix time is not the number of seconds since epoch

I assumed that is (part of) why he ended up wanting to build his own. The way UNIX time handles leap seconds was arguably a mistake, GPS (and Galileo) time does it right; have the leap second information in a separate field. So as a time scale I imagine OPs one would be similar to GPS but with different epoch. Of course everyone loves having informally defined ad-hoc timescales around


> Unix time is not the number of seconds since epoch

In fairness, he said to count the number of seconds since the Epoch. That is independent of UTC.

That means using e.g. TAI. Unix time also ignores leap seconds since it counts the number of seconds actually elapsed.


> Unix time also ignores leap seconds

hence it counts the number of seconds that would have elapsed, if they didn't exist. In effect, it's timescale has time that never happened, and time that happened twice.


You can still have problems.

It may be determined that the computer's clock got too far ahead. For example, it booted and you cared about time, but NTP hadn't yet made corrections. Suddenly the time runs backwards.

Leap seconds may get interesting too, especially if you have to predict ahead or if the OS isn't updated often enough. (there is a 6-month warning) If you want to call leap seconds an issue for humans, then you aren't using UTC at all. You're using TAI. Software interfaces often ignore the distinction between UTC and TAI, and even between UTC and UT1, preferring to pretend these issues don't exist. POSIX is in conflict with international timekeeping, effectively requiring that there are zero leap seconds.


> Suddenly the time runs backwards

It is my understanding that the way NTP deamons work is that time is never adjusted backward. Instead, the ticks are "slowed down" on the local machine until it is in sync with the NTP time. However, if the difference is too great then I think NTP deamons might refuse to correct the time all together. So then, if my understanding is correct, your machine is "stuck in the future". But it will never make a jump backwards because of NTP.

However, I am not familiar with the intricate details of NTP so do take this with a grain of salt.


My understanding (i am also not an expert!) is that common NTP implementations will do the slowing down ("slewing") to correct small errors, but will just change the time ("stepping") to correct large errors. It will even do this if that means time going backwards.

Subject to configuration, of course. man ntpd [1] says:

> Sometimes, in particular when ntpd is first started, the error might exceed 128 ms. This may on occasion cause the clock to be set backwards if the local clock time is more than 128 s in the future relative to the server. In some applications, this behavior may be unacceptable. If the -x option is included on the command line, the clock will never be stepped and only slew corrections will be used.

[1] https://linux.die.net/man/8/ntpd


timesyncd also does this for "large offsets", but what "large" is is neither configurable nor documented, but in the source:

    /*
     * Maximum delta in seconds which the system clock is gradually adjusted
     * (slewed) to approach the network time. Deltas larger that this are set by
     * letting the system time jump. The kernel's limit for adjtime is 0.5s.
     */
    #define NTP_MAX_ADJUST                  0.4


> neither configurable nor documented, but in the source:

Build it yourself and pass -DNTP_MAX_ADJUST=XX


Leap seconds are only a problem for wall clocks (localization) and deciding whether to call something utc or tai.

The ntp case is contrived. Either you care and wait until ntp has connected to do your stuff. Or you care and don't let ntp rewind and instead smear. Or you don't care and deal with the consequences.


Leap seconds affect Unix time too, because leap seconds are excluded from "number of seconds since Unix epoch". You can't measure the length of time intervals spanning leap seconds with simple subtraction.


> Leap seconds affect Unix time too, because leap seconds are excluded from "number of seconds since Unix epoch".

They are excluded from "unix timestamps". They would still be part of the number of seconds since Unix epoch.


What does that mean and how would that work?

AFAIK, unix time skips a beat or repeats itself to remain in alignment with UTC, and just keeps chugging along.


"Number of seconds since the unix epoch" describes a physical measurement (that can be related directly to TAI) that would not be affected by leap seconds. You could store that number to allow you to recover TAI timestamps (I'm not aware of any OS that does this, but there's no reason it wouldn't be possible).

Unix time does indeed repeat itself so as to remain in alignment with UTC. But unix time is not the number of seconds since the unix epoch.


Thank you, I think I understand, but disagree on terminology.

Leap seconds are not excluded from number of seconds since Unix epoch, interpreted on a physical, TAI, UTC, typical local time scales.

However, they are indeed excluded from "number of seconds since Unix epoch", when interpreted in unix time. In unix time, those seconds simply don't exists (never happened) and the events of those seconds are smooshed sometime. Unix time representation forms a (quirky) timescale.

I'll quote a few excerpts from 'date' man page of linux, openbsd and for gettimeofday:

Convert seconds since the epoch (1970-01-01 UTC) to a date

Print out (in specified format) the date and time represented by seconds from the Epoch.

The time is expressed in seconds and microseconds since midnight (0 hour), January 1, 1970


Hmm. The first definition I saw of "unix timestamp" explicitly defined it as "86400 * number of days since 1970-01-01 + number of seconds since midnight", which I thought was clever and clear, but I can't find it now. Having looked up the POSIX standard at https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1... it seems you're right and I'm wrong; they define "Seconds Since the Epoch" as "A value that approximates the number of seconds that have elapsed since the Epoch...". In my view this is an extremely poor choice of terminology, because "seconds since the epoch" should have its plain English meaning of the number of SI seconds that have passed since that event; defining some "second" that is almost, but not quite, an SI second seems like a recipe for disaster.


Are they truly? There must be a good reason but I can't fathom what it could be


That good reason is that there is no general "formula" for leap seconds, unlike for leap years, they have to be looked up. So you can't do "offline" date calculations if they included leap seconds.

I think that UNIX time stamps are generally a very good approximation, and if you are comparing long enough time intervals for the error to get over one second, and/or that error to matter, you are doing something wrong anyway.

For exact time interval measurements that you have to get exactly right, don't use UNIX time stamps.


The lookup database is incredibly small and updated very infrequently. Certainly smaller and less frequent than tzinfo which is also included in the kernel


AFAICT leap seconds are announced about 6 months in advance (January/June). How would you process times that are to occur after a leap second that may or may not be?

You could store something as '2022-02-02 02:22:22 UTC', but try storing it as seconds since epoch, if you don't know if there will be leap seconds. Not to mention software that may be years or decades old.


Because otherwise you would get annoying off-by-27 errors whenever you did time() % 60.

https://en.wikipedia.org/wiki/Leap_second#Binary_representat...


It depends on the purpose of the app, but you often have to store user entered times with time zone information. The rules for things like daylight savings time can and do change, and you don't want future scheduled events drifting by an hour.


Let's not forget events spanning time zones. Just some random thing that came to my mind: how would you handle calendar entries where half the participants made a DST transition since the entry was created and the other half didn't? This happens for example when half of the team is in the US and the other half in the EU. The transition dates are a week apart.


I remember seeing a thick dead tree type of book with the history of time zones in the US, for figuring out times in historical documents when things were less standardized. It was practically the size of a phone book; I think it probably covered county level history or something like that.


I have a book covering, among other things, Indiana time zones for a few years during IIRC the 1960s.

It’s frankly amazing how much they changed every year. Different counties, and sometimes towns/cities within counties, would jump back and forth year to year. It would have been awful to manage if computers had been more important at that time.


None of these edge cases are solved by using off the shelf libraries instead of running your own.


None of these edge cases have a chance of being handled correctly without an accurate time zone database to detect the problem at all. Good luck maintaining that on the side! I am fairly certain that you would get it wrong. There's a reason why most software relies on zoneinfo.


Yes, as I said above, if you need to localize then you'll need more detail.


When do you not?


When you need some system activity to happen regularly (e.g. every 10 minutes) but not at a specifically human-meaningful time. When you need to know what order events occurred in or how far apart two events are, but don't need to correlate those times with external events (or can easily establish "system" times for any relevant external events). You can cover a lot of cases without having to touch "human time" at all.


I've come to the same conclusion as you: Keep time as a purely monotonic integer for everything and convert that as needed to display to humans.

This also pushes all the madness to the edges and out of the business logic.

That being said, this works well for applications that are not "date intensive" so to speak. If your business logic has to deal specifically with calendar dates, e.g., monthly events, then you have to deal with calendar months and all that this involves, including explicitly dealing with the 29th February.


You want to store both local time (and timezone and/or some proxy for location if possible) and Unix / UTC / integer time. The latter is what your application relies on 99% of the time, but if, say, a given country gets rid of daylights savings time (as Brazil did last year), having the local time is helpful for recomputing your Unix time.


The monotonic integer approach doesn't work for most healthcare applications. Due to safety and compliance requirements we typically need to record both the local time and the zone offset which applied at that instant.


This is surprising for event recording since they should be equivalent but time + zone is strictly more likely to be messed up. E.g 2:30 a.m. is two times during daylight changeover so you must correctly specify est or edt.


You're assuming a source of monotonic nanotime is easy to get and will always be available. This is not the case. As far as I know you have either "wall time" as usually defined, with all the problems associated with clock drift, NTP syncs, computer going into sleep mode, etc, or "nanotime" which is some time delta from some arbitrary reference and which can only be used to compute time differences and not arbitrary instants in time. One can not be converted into the other easily, or at all.


You use the arbitrary reference and sample the wall clock a couple times to interpolate a best effort offset from utc 0.

If you care more than this, you'll be displeased with off the shelf solutions too


Your percentages are backwards, IME. Timeouts and logging and such are definitely not “80% of use cases”. Interfacing with humans and human systems are.


I disagree. Time is pervasive and will exist in every application in some way. It will only matter to the user in a small subset of them



Immediately thought of Tom Scott's Computerphile video on time & timezones:

https://youtu.be/-5wpm-gesOY

10m12s watch. Informative and entertaining.



When you encounter someone saying "no one should ever", you can substitute "fewer than a half dozen groups of people should tackle this problem (in systems they want to use in production). It will take each such group a tremendous number of hours, involving a multi-year process of slowly finding edge cases and missing functionality."

If that's not true, then there's room to complain, but dates and times fit the bill. In some ways they're worse than other "harder" problems, because people are more likely to think those harder problems are too hard for them. And while the vast majority of companies don't need a proprietary database, it's more likely to be a competitive advantage than your own datetime library.

I think I could eventually write a good datetime library. But I certainly should not, unless I decide that's going to be one of my major efforts to help a language that doesn't already have one.


What if people tried to speak precisely for effect instead of hyperbolically for emphasis?


I believe accurately conveying identical information in a manner everyone can understand is an impossible, or at least unreasonable, burden to place on individuals.

Instead, what if people tried to hear graciously and "assume good faith"?

The quote is ripped from HN's guidelines.


Can you please explain more about how your response relates to my comment?

For instance, I've not placed the burden of "conveying identical information in a manner everyone can understand" on anyone, nor have I assumed bad faith. So it seems like a weird comment to tack onto mine, but I am assuming I just don't correctly understand.


Hyperbole and precision can get tricky in human language because different cultural groups have different language encodings for the same sets of words.

For example, if you live in London then "9 in the morning" means when the world synchronized clocks agree that, locally for you, the time is 9am. But if you say "9 in the morning" to someone in Belize, it means "first thing after you are finished with your morning and ready to start your day," which can mean 1pm in some cases.

Here's a lovely article on these kind of time-keeping differences, around something that you might expect to have a precise meaning:

https://www.businessinsider.com/how-different-cultures-under...

More to the point at hand, however, you suggested that people not speak in hyperbole but instead speak accurately. Although you can request that others adjust their use of language while in your presence to better meet your needs for a certain kind of precision, policing other people's language isn't possible. However, re-interpreting what people say into what they mean is somewhat possible for an astute listener who understands the context.


You are making my simple statement really complicated.

I'm not walking around demanding people change their communication to accommodate me, I'm suggesting that trying to speak precisely can be a useful exercise.


I assumed you were being precise when you asked "why can't people speak more precisely" and also when you asked "can you explain how your comment relates to mine?"

I am not the person you originally responded to, fwiw, and I agree that trying to speak precisely is a useful exercise, even if I believe it is impossible except in highly formalized languages.


I wrote "What if people tried to speak precisely", not "why can't people speak more precisely".


Ah. My apologies. Hope you enjoyed the article at least.


The sibling replies by IggleSniggle I fully endorse as supporting my meaning in the original post.

As to explaining more about my thought process: You asked a hypothetical "what if" question which, given the question itself is imprecise, I interpreted as you wishing information was always conveyed to your desired precision/accuracy, and extrapolated that (given this is a public forum) into general communication.

I shared my thoughts, specifically that it seems impossible for a person to always communicate perfectly to an unknown audience, and offered a different hypothetical. The last line explains the punctuation use in my "what if."


Precision of understanding would go up and emotional investment in a topic would likely be more proportional to true stakes?

Sounds boring but functional.


It's not only about writing your own library though. For example, it's often not reading the documentation properly.

In Python, if you take a datetime, and call .replace(year=X) on a datetime for Feb29, it'll throw a ValueError.


I think .replace() is a mistake, and it shouldn't exist in the first place. The way dates work, replacing a single component is almost always going to create problems in specific cases.


And then you have to implement business directives with “the same date in 2025” in documents already signed by all parties (and no one got confused, except math guys). Replace is not a mistake, it just should state what it does, so that a programmer could test and use it.


It does state what it does, but people rarely read documentations, and unlike static languages, Python doesn't force you to deal with the exception. And since it's a very rare problem, most people don't catch the bug.


What if you take March 31st and add one month to it? (I assume you can do that somehow)


Not with the standard library. (The "timedelta" class only expresses in units up to days, as it is ambiguous how long a month is.

There's a nice package called "python-dateutil" that includes a "relativedelta" class; adding a month to March 31 results in April 30:

  In [7]: datetime.datetime(2020, 3, 31) + dateutil.relativedelta.relativedelta(months=1)                                
  Out[7]: datetime.datetime(2020, 4, 30, 0, 0)
Adding a year to a leap day:

  In [8]: datetime.datetime(2020, 2, 29) + dateutil.relativedelta.relativedelta(years=1)                                 
  Out[8]: datetime.datetime(2021, 2, 28, 0, 0)
The exact duration that relativedelta adds depends on what you add it to. (Hence the name.) But the results tend to match up with human expectations.


You get April 30. Idk about all libraries, but date-fns and few others I worked with do it right.

Also, if you add one month to Apr 30 and two months to Mar 31, you’ll see that month addition is not commutative-y and one should operate on distances from a base date, not in an incremental way.


Even if you replace it with another leap year?


No another leap year would be fine. But yeah a very common (wrong) pattern is, if you want to find the same day a year in advance, is to do `d.replace(year=d.year + 1)`, and that would break on Feb 29 only, so one day every 4 years. It's a very common pattern unfortunately.


It returns a new datetime object which includes its own validity check and raises an exception for a bad leap day the same way it would for March 32nd or Blurnsuary 12th. (However, year must be an integer in [1, 9999].)

Edit: So, another leap year would be fine.


How about C rtos? How should that be handled?


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.


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.


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.


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

[1]https://kingcounty.gov/depts/transportation/metro/schedules-...


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.


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


It's already here...I saw an issue a few weeks ago a bunch of production systems failed upon seeing a certificate signed by a CA root that expires after 2038.


Or: We're now in the equivalent of 1982. Plenty of time to worry about it later!


and only heading in that direction


Speak for yourself.


what does this mean? you're okay if you're using 64bits? just briefly skimmed the 2038 wikipedia page, mentioned 32bit


If you store time as seconds since the Unix epoch (1st Jan 1970), you'll overflow a 32 bit unsigned integer in 2038 (around March iirc) and time will suddenly be back in 1970 again. I believe the Linux kernel did some work to circumvent this on 32 bit systems in a recent release, but if you're running an old 32 bit system you're probably out of luck.


Actually, it's signed 32-bit integers that overflow in 2038. Signed integers have been used because people wanted to store dates earlier than 1970 too.


And probably because signed integers are a default choice in certain languages and/or maybe on certain architectures.

Java, for example, famously doesn't even have an unsigned 32-bit integer primitive type. (But it has library functions you can use to treat signed integers as unsigned.) Ultimately not a good design choice, but the fact that it actually wasn't that limiting and relatively few people care or notice tells you that many people have a mindset where they use signed integers unless there's a great reason to do something different.

Aside from just mindset and inertia, if your language doesn't support it well, it can be error-prone to use unsigned integers. In C, you can freely assign from an int to an unsigned int variable, with no warnings. And you can do a printf() with "%d" instead of "%u" by mistake. And I'm fairly sure that converting an out of range unsigned int to an int results in random-ish (implementation-defined) behavior, so if you accidentally declare a function parameter as int instead of unsigned int, thereby accidentally doing an unsigned to signed and back to unsigned conversion, you could corrupt certain values without any compiler warning.


> Ultimately not a good design choice, but the fact that it actually wasn't that limiting and relatively few people care or notice

It was a horrific design choice, and people do notice and do hate it.

Not so much because of how it applies to ints, but because the design choice Java made was to not support any unsigned integer types. So the byte type is signed, conveniently offering you a range of values from -128 to 127. Try comparing a byte to the literal 0xA0. Try initializing a byte to 0xA0!

In contrast, C# more sensibly offers the types int / uint, short / ushort, long / ulong, and byte / sbyte.


> out of range unsigned int

AFAIK, that doesn't exist? Otherwise, unsigned would also have UB?


I think he's referring to an unsigned integer value that's out of range for the signed integer type of the same width—usually a bit pattern that would be interpreted as a negative number when cast to a signed integer type, but which negative number is not defined by the C standard because it doesn't mandate two's complement representation.


I'm reading that as, the value of the uint is out of range for the int.


Ah yes, thanks for the correction :)


One of the biggest mistakes in IT ever, in my opinion.

I'd even go so far to say that defaulting to signed instead of unsigned was also one of the biggest blunders ever. I would've never defaulted to a type that inherently poses the risk of UB if I have another type that doesn't.

Though it's also possible that precisely that was the reasoning for it.

Some more thoughts on unsigned vs. signed: https://blog.robertelder.org/signed-or-unsigned/


Related is NTP which overflows in 2036 because it uses unsigned 32-bit seconds since 1900.


That's technically correct, but NTPv4 has the concept of eras [1] which gets incremented when that number would normally wrap.

[1] https://tools.ietf.org/html/rfc5905#section-6


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()



Interesting. Given the documentation of strptime, I would have expected that to not be possible at all (since constructing a datetime without a year isn't possible directly)


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


  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


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


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


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?


Happy -1st of February.


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.


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



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

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)



I was about to ask why you didn't use a timedelta, and then found out that datetime.timedelta doesn't support anything greater than week.

It also has this incorrect example: https://docs.python.org/3/library/datetime.html#examples-of-...


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 :)


I love the image, BTW! :)


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 :)


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.


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.


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".


Over 4 years there are a lot more apps and a lot more reliance on apps. There's lots more spaghetti out there since 2016 so more likelihood of bugs. I assume 2024 will have more.


Leap days are an edge case by themselves, it doesn't need any extra thing special about it.


I do wonder whether the fact that some systems aren’t really 4-digit-year compliant is making this uglier this year. I know Splunk got hit with a 2020 bug.


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.


relevant: falsehoods programmers believe about time https://news.ycombinator.com/item?id=4128208


LogRocket is reporting every date as Feb 28, 2020.


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


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) 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 :-)


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!


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...

http://www.madore.org/~david/computers/unix-leap-seconds.htm...

https://tools.ietf.org/html/rfc5424

https://cr.yp.to/libtai/tai64.html

https://cr.yp.to/libtai.html


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.


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.


Precisely, get yourself five digital devices that track time, and see how many of them are even accurate to the second. Probably none of them. But implementation of leap seconds would only make sense on systems that at least fulfill that minimum criteria.

Leap seconds are things for stock market servers, atom clocks, scientific devices and the like.


Is there a real need for syslog to handle leap seconds? As that email you linked to points out, most implementations are likely to screw it up to some extent; requiring everyone to ignore leap seconds seems to narrow the range of possible behaviors. It also strikes me that syslog timestamps aren't something you should be depending on too heavily in the first place, especially not for things like calculating precise time durations between separate messages.


Two wrongs don't make a right, but three lefts do.

Throwing shade without evidence doesn't demonstrate professionalism, it demonstrates laziness.

Correlation on high-volume production systems requires precise timestamps all the time.


> Throwing shade without evidence doesn't demonstrate professionalism, it demonstrates laziness.

Who's throwing shade?


it's bad enough guessing dmy or mdy, without wondering if we earned interest on that 10 billion euros that appears to have spent an entire second in a transit account earning 2.7% per annum (legal think 'annum' implicitly includes the leapsecond, an assurance resting on dozens of assumptions and misunderstandings of many ibscure treaties and standards and policies, modulo the case law in jurisdictions where precedents can redefine)


What does any of that have to do with syslog?


assuming 'what happened to X' involves looking in logs


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


Just solve it the Google way: https://xkcd.com/2266/


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.


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.


My Timex Expedition watch also skipped to March 1.


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.


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


In mechanical watches, that's the difference between an annual calendar, and a perpetual calendar. Annual calenders don't take leap years into account, perpetual calenders do.


Timex Ironman bought within the last two years, almost certainly less than $20, says 3/1 Sat today. The numbers on the back might say "029 36". Or maybe "029 56". I'm sure its innards are the same as most of the other sub-$20 Ironmans.


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


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)


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.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: