Hacker News new | past | comments | ask | show | jobs | submit login
Fixing a Small Calc.exe Bug (petertissen.de)
279 points by cryoW0lf on June 19, 2019 | hide | past | favorite | 117 comments

This is hilarious!

Some of you might recall how all Zune devices died on 12/31/08.

Windows_Globalization_Calendar_AddMonths_System_Int32 was the root of it all, apparently.

(For further reading on the Zune outage: https://techcrunch.com/2008/12/31/zune-bug-explained-in-deta...)

Some similar stories: "unit tests fail when run in Australia" [1] and "the case of the 500-mile email" [2].

Many interesting debugging stories are collected in this GitHub repository [3].

[1] https://github.com/angular/angular.js/issues/5017

[2] http://www.ibiblio.org/harris/500milemail.html

[3] https://github.com/danluu/debugging-stories

> unit tests fail when run in Australia

I've found countless ones of these. Some of them in date/time/timezone handling libraries.. :/

Thank you so much.

Also reminds me of the old "Printer won't print on Tuesdays" bug


I mean, this is a great example of how complex systems exhibit unpredictable, emergent behavior. You think you are doing some process X (say, printing), but it turns out that X relies on some more generic layer Y (a file utility layer) which is running a set of arbitrary filter functions that you have no idea exist. One of these filters then happens to check for some magic strings in your data. And these hardcoded strings turn out to be strings that can also occur in unrelated kinds of data...

What's the underlying issue, really? Is it that detecting file types by checking for magic strings is always a bad idea? Or is it something more basic about the architecture here, more like the leaky abstractions problem?

The problem here is that the printing pipeline uses the ‘file’ tool to detect what the data to be printed is (e.g. PostScript), but ‘file’ can only ever guess the content of a file. It is a heuristic, and as soon as you have a heuristic, you will always run into cases where it gives a wrong result (otherwise it’s not a heuristic anymore, obviously).

So, I would actually disagree with the author that it is strictly a bug in the ‘file’ utility, as there is simply no completely reliable way to determine intended file type by just looking at the content[1]. You have to transmit that information out of band, and this is why we have MIME types sent through Content-type: headers in HTTP or email headers. File extensions are another method (though also ambiguous: .TXT and .EXE are almost certain, but a .DB or .BAK can be one of many things).

CUPS is probably applying this heuristic for convenience (“send anything to your printer and it just prints!”), at the cost of correctness.

[1] A simple proof for that is the existence of “polyglots”, which in this context are source code that happens to be a valid program in two or even more different programming languages (often doing the same or similar things for sport). Were CUPS to differentiate printing based on the programming language, e.g. by applying different syntax highlighting, it would not know what language to choose, and without out of band information there hardly even is a “right” answer. In practice though you can detect a file type more reliably than ‘file’, but at much more complexity than “let’s check some magic numbers in some offsets to give the user an idea of what this might be”, and it still wouldn’t be perfect.

It still is a bug in the data for file, though. As the respective bug states, the spaces should have been escaped and so file actually misidentified everything with »Tue« in that place of the file contents as the wrong type.

Wow that’s a zinger! The underlying issue with the file utility is bonkers! https://bugs.launchpad.net/ubuntu/+source/file/+bug/248619

> This bug is causing printing using the brother cups drivers to fail on Tuesdays.

Absolutely loved reading through the bug! haha

Technically, the Zune bug didn't kill the device. The bug sent it into an infinite boot loop until the following day when the leap year calculation could return.

But yes, the difficulty in verifying a correct implementation of date and time calculation calls out for a standard test apparatus to "prove" correct results for a defined scope.

I once stupidly implemented a "date math" feature, and after introducing numerous bugs in production opted to use https://svn.python.org/projects/sandbox/trunk/datetime/test_... as my ref. test (which exposed numerous other bugs). Likely not full coverage of all date-time related bizarrities, yet a useful resource.

> I once stupidly implemented a "date math" feature

I believe this to be one of those rites of passage for any programmer.

That and/or "rip out this idiot date math library written by your predecessor and try to use an industry standard library instead". Especially the karma of first one then the other.

About the only way to keep your sanity is to treat dates in only one of two ways, depending on the context:

1. As a string you don't touch

2. Convert to seconds_since_the_epoch immediately. Convert back only when necessary (when printing to the screen or sending to a SQL query for example).

This calc problem is somewhat unique because months and years aren't fixed length and it's probably bad if your program says Feb 1 to March 1 is less than a month, or Jan 1 2000 to Jan 1 2001 is a year and a day. Or if Jan 1 2001 to Jan 1 2002 is a year and a quarter of a day. And then you have to account for those occasional years that are divisible by 4 but are not a leap year. And then you have to account for when someone asks for the difference between September 1 1752 and September 20 1752. Pretty soon you're remembering why rules 1 and 2 exist. It should be noted that calc.exe gets this wrong in many ways.

> Convert to seconds_since_the_epoch immediately.

oh my sweet summer child

For date and time, Arrow is a lifesaver in python


Impressively, they did actually merge the fix: https://github.com/microsoft/calculator/pull/553

It's good to see that Microsoft's "open source" programs aren't just code-dumps

For what it's worth, VS Code merges a tremendous number of PRs from the community, despite having a dedicated team: https://github.com/microsoft/vscode/pulls?q=is%3Apr+is%3Aclo...

New MSFT really "gets" open source. It's an exciting time.

Sure- but that was conceived from the beginning as an open-source project, and isn't even truly "compiled"; it's designed to be hackable. Calc.exe is a core Windows utility, and was open-sourced after the fact.

Chakra was a many years old code base that was open sourced a few years ago, and we got a large number of pull requests from the community merged and shipped with windows and in chakracore releases. Object.fromEntries is one that immediately comes to mind but I know there were more. It happens across a lot of our projects!

Yeah, with the current CEO Microsoft got really active in the OSS Community. Still MS with Telemetry, like in the pre-compiled VSCode, but it's the right direction!

I've also been extremely pleased with their acceptance, communication, and turn around with bugs filed on the mssql-jdbc driver. Code is typically fixed and new versions published fairly regularly. I hope they keep it up.


Time is hard. But like what does it even mean to say something is 4 months away when the months could be different lengths? 4 months is a shorter time if that period includes the end of February. This fixed result is strange but does it even matter?

what does it even mean to say something is 4 months away when the months could be different lengths?

The same thing it means when something is 2 years away when the years could be different lengths?

The month is a well-defined unit within the context of a certain calendar. And since a normal date representation contains the month as a singular component, month additions are usually well-defined (the obvious exception being, adding N months to yyyy-mm-31 -- the result is undefined, though most people will assume it to refer to the first day of the month following).

There is no need to bring mathematical strictness into it, because months are never used for that purpose. Moreover, no calendar system is built for mathematical strictness anyway, so arguing for that kind of strictness is like building a house on quicksand.

> though most people will assume it to refer to the first day of the month following

I wouldn't, I'd assume it's the last day. For me, any date that is "two months after" any date in July should fall in September, with the days longer than the target month scaling down. So two months after July 31 should be, to me, September 30.


    $ date -d "2019-07-31 +2 months"
    Tue, Oct  1, 2019 12:00:00 AM
Suggests I'm in the minority.

    > (date 2019-07-31).AddMonths(2)

    Montag, 30. September 2019 00:00:00
.NET agrees, though :)

For software libraries it's a bit of a pickle, though, as you have the trade-off between »do what an end user would expect« and »do exactly as told, and let the programmer deal with the weirdness«. Admittedly, adding months is something that I'd rarely do as a programmer when the context isn't what a user would expect. Adding 30 days can be done in much the same way if that's really what's intended.

Interesting question, though: Is 2 months from 2019-07-28 2019-09-28 because the day part is the same, or 2019-09-27 because it's three days from the end of the month? ;-)

You're right, but I think the problem does arise for "four and a half months away". That actually requires you to pin down how many days are in a month.

There is -- or should be -- an understanding that when such a phrase is used, it is not intended to be taken as a precise duration. Furthermore, for any of these expressions, while there is not necessarily just one moment in time that it is equivalent to, there is a vast range of times that it definitely does not denote.

It only requires you to know how many days are in each month, and you have to know what you’re counting. That’s the problem with calc: it discards the semantics of “how I got here,”

I think it's more complicated, and depends on assumptions made by the speaker and the listener: if I say "two and a half months from now", is the half-month equal to 15 days or 14? If the target duration is February, perhaps I'd assume 14; but otherwise I probably assume 15; but what if February is one of the whole months in between?

In my experience (in my culture), we avoid these problems by just assuming that we'd never convey a precise date in this manner: anything more than a week away we tend to convey with the exact date (March 15th); or, we might say "exactly two weeks from today" or "exactly one month ago" (implying the same numerical day-of-month, regardless of how long each month is), but I don't think we'd ever say "exactly one and a half months from now".

And this is why calendars are hard: it's as much (or more) anthropology as it is arithmetic.

True, but you wouldn't use that in a context where precision is required. You'd say something like '31 weeks' if the specific duration mattered, or just the date itself. When the exact duration isn't particularly relevant, the ambiguous 'four and a half months' works just fine.

One of my first gigs we did a LOT of work with a Sybase database. Backend code frequently made calls to the Sybase database for no reason other than to use the DATEADD and DATEDIFF functions because they were so reliable and easy to use (except for being in a database).

Dates are actually always durations. But most time libraries design fail to aknowledge that. When you say something is 4 months away, it means you're probably happy with a 1 month resolution. 4 months from 31 July is November. 4 months from any day in July is always November.

But 4 months from July 31 is Dec 1.

Talk to any non programmer or mathematician on the street (aka your users) and they’ll expect the answer to be Nov 30.

Reminds me of the old joke, why do programmers confuse Halloween and Christmas? Because DEC 25 is OCT 31

that's 123 days from July 31 not 4 months from July 31

Agree, hard even though one may say a minor program.

Interesting. This bug is reproducible in the 'new' Windows 10 calculator store app, but not in the old Windows 7 calc.exe. So looks like a regression, unless they completely re-wrote the whole calculator app (for which there seems to be no reason).

> unless they completely re-wrote the whole calculator app (for which there seems to be no reason).

That's actually exactly what happened. The original calc.exe (in all versions up to Windows 8.1) came from Windows 1.0, in plain C, very seldomly updated over the years.

The one in Windows 10 is totally rewritten in C++ using the "Universal Windows Platform" as an API rather than classic Win32.

The Windows 10 UWP calculator retains the math engine from before, which actually has been rewritten. The ancient calculator used standard floating-point math, with the fun problems that entails, like 0.1 + 0.1 showing 0.19999999998. The current engine uses rational numbers as long as possible and configurable precision for trigonometry, roots, etc. It also got further upgraded over the years so that the root of a perfect square is always an integer.

As for the bug in question, it could not have happened in the old calculator, as that one didn't have the date math panel.

So for the UWP version they merely redid the UI and added a few things. The actual calculator part is still the same as before (just open-source now).

Are you saying that the 'old' (i.e. Windows 7 calculator) didn't have a Date calculation panel? Because I'm seeing it right now in front of me.

I had no idea that was in there along with all of the other things like a PMT calculator and unit conversion. I would normally switch to the scientific calculator after getting a fresh Windows 7 and never open that menu again.

Ah, haven't seen the old calculator in years, so wasn't aware that it was in there. Then they only redid the UI.

I also don't know it until today, but calc.exe in Win 7 does have a date calculator.

I know some people like to comment on the headline rather than reading the content but that comment is literally the first line in the article:

> tldr; saw a Windows Calculator bug on reddit. Since calc.exe was open-sourced I thought I’d try to find the bug and fix it.


There are almost 8 billion people in the world. I think 17,000 people is a small number. And geeks are going to geek and nothing geekier than checking Microsoft Calculator, a simple project that everyone can relate to.

I think you are right. Another possible explanation would be a big conspiracy though, and probably the government is also involved somehow.

I just tried the same thing on Windows 7 and got some curious results:

  From July 31 2019 to Dec 30 2019: "5 months; 6 days", 152 days
  From July 31 2019 to Dec 31 2019: "5 months", 153 days
I agree that there are 153 days from July 31 to Dec 31 (aug+sep+oct+nov+dec = 31+30+31+30+31 = 153) but I'd say that there are too many Februaries among the months in the first result.

Somewhat offtopic but this reminded me of a tutorial from ages ago which showed how to add line numbers to notepad.exe . It was part of the things I followed while learning about cracking and reverse engineering.

I remember being super happy when I replicated the functionality, using wdasm32 and HIEW to patch my notepad code.

Link to tutorial?

Oh man, this was ages ago, so I don't even know if it is still available on the web.

The best I could found is a similar tut where they are adding other features to notepad:


This brought me lots of memories, learning from +ORC, tKC and +HCU was the bomb back when I was in University.

I could not find the one specific for adding line numbers though.

"Never work with children, animals, or calendar math.."

It should be clarified in the title that it is about the UWP calc not the original Win32 calc.exe.

This blog is the general information for the feature. You got a good work for these blog.We have a developing our creative content of this mind.Thank you for this blog. This for very interesting and useful.


Why did this change get merged without a unit test?

I've noticed a new way GitHub can show tests. There's a new checks tab at the top of the issue. There[0] you'll find thatall unittests were successfull.

[0] https://github.com/microsoft/calculator/pull/553/checks

I think he meant that unit tests were not added for the scenario detailed.

One thing I find interesting, and which I like: As far as I can tell, this commit was merged without the submitter needing to sign a CLA or otherwise assign copyright.

The author signed a CLA. "7 checks passed", and if you click "View Details" you see:

> license/cla All CLA requirements met.

Also mentioned in https://github.com/microsoft/calculator/blob/master/CONTRIBU...

That's good to know, and annoying, but also a little confusing.

The page you linked indicates that a bot will comment on the PR. I am looking at https://github.com/microsoft/calculator/pull/553, but I do not see any bot-generated comments, other than the "All CLA requirements met." status line you mentioned; and I do not see a "cla-signed" or a "cla-not-required" tag on the PR.

Still annoying, regardless.

It says "you only need to do it once for Microsoft projects on GitHub". So maybe the author signed it before.

Microsoft GitHub projects stopped spamming about CLA a while ago. It will only add a CLA comment to the PR now if you haven't yet signed the CLA and CLA is required. Since this is a one liner, CLA isn't required.

But it looks like the CONTRIBUTING.md file wasn't updated to reflect this change.

The PR is not merged yet. They better do it before July 31st!

It was merged "yesterday", some 28 hours ago, ref https://github.com/microsoft/calculator/pull/553.

eh, it still gives a wrong result, except one is visibly wrong and the other subtly wrong.

It's merged but not released yet I guess.

I don’t think we’re at the point where Microsoft is doing Continuous Deployment to millions of Windows PCs for calc.exe :)

A "month" is a poorly defined unit of time and shouldn't be used to describe a duration.

Also "blue" is a poorly defined description of color and shouldn't be used to describe a color.

The truth in both cases is of course that while the meaning is contextual and imprecise, it's still useful because people generally don't use precise measurements in their day-to-day activities. When someone tells me that something will happen in a month, I take it to mean something in the ballpark of 28-31 days, and that information can be useful to me.

Because of leap seconds, the only really unexceptional unit of time is seconds, but someone telling me that something will happen in 2592000 seconds isn't particularly useful to me.

The calculator should then show "approximately N months" -- showing "N months and M days" is nonsensical if "M" has no useful precision

In the context of the calculator, you already know the start and end times, and it shows the duration in terms of days (which is unambiguous given that we know the interval when the duration takes place). I think that it's clear that the duration in terms of months, weeks and hours is there just to put it in more easily graspable terms, at the expense of precision.

As it happens - neither is a day, or even an hour. The exceptions are rarer, which only makes bugs even more insidious when they do happen (like dealing with leap-seconds, for example. Some hours have 3601 seconds!).

ie: https://infiniteundo.com/post/25326999628/falsehoods-program...

While this is technically correct is it more accurate to say that some minutes have 61 seconds? Or perhaps that some seconds have two seconds?

i don’t think either is correct. the leap second is an extra second, that’s it. the notation for the extra leap second is lacking so we are “forced” to call it 23:59:60 but that doesn’t mean that minute has 61 seconds. it just means after that minute but before the next one, there’s an inserted second.

it’s far more sane to define it that way than to say the duration of a minute (and hence hour, and day, and week) is variable.

I see what you mean but how do we address that leap second specifically? How do we describe an event that happened during the leap second?

Well, it could also be the case that some minutes have 59 seconds, although afaik it hasn't happened yet.

Given the trend of leap seconds, I don't think it will happen (unless we do really stupid things to the earth). See https://what-if.xkcd.com/26/

The Python "dateutil" library includes a "relativedelta" that works how one would typically expect. Though, I've usually used it to add known deltas, not to subtract two dates and arrive at a delta, so IDK if it supports that.

(It has rules, of course, for dealing with 30 day months when you're on the 31st and you add a "month". The normal timedelta class from the standard library doesn't do "months", since, as you note, it's not really a measure of time.)

In Unix timestamps, over a leap second, a difference of 1 Unix second is occasionally 2 seconds long.

This is what we do. "One month and 3 days" is not a well defined duration, but it's what we use in casual conversation.

The date that occurs "in one month" is the same day day-of-month in the following month. The same is true for years. Between christmas day one one year and boxing day the next year, it's "one year and one day", regardless of whether that actually means 366 or 367 days.

If on christmas day you say "In one year and one day we'll travel to australia", you aren't really specifying a duration but a date in the future. Listeners understand that regardless of whether the following year is a leap year, what you are describing is boxing day the following year.

While you could specify the same thing with an more precise interval such as 367 days, that's actually less useful as a way of describing the date of the event, but a more exact way of describing the interval of time to the event.

So what the calculator shows, is how to communicate a date to another human, in the inexact way that they expect. This is actually a tricky problem in programming (as is obvious by this bug)

Months are pretty well and exhaustively defined. There are 13 of them. It's just that it's not enough to say that there is a 3-month interval. You need to specify either the dates or the months.

To say months shouldn't be used is sort of like saying that complex numbers or vectors shouldn't be used. They are absolutely useful. We just deal with their component parts separately.

I think that you are missing the point. GP means to say that "a month" is poorly defined as a unit of duration, specifically. Not even ISO 8601 specifies what exactly a month means in terms of duration, despite specifying a duration description format that includes months.

In some cases you can take "a month from now" to mean the same calendar day of the next month. This leaves what a month from January 31 means ambiguous, and also dependent on when the duration starts.

You can take an easier route and say that a month is 30 days. Still, the duration is ambiguous because it depends on when it takes place (i.e. an interval), because of summer/winter time adjustment. You can say that it's 720 hours, but that's still dependent on the start of the duration because of leap seconds. In the end, the only way to describe a duration unambiguously regardless of its start time is with seconds. There is no such thing as a well defined duration for any other unit of time.

A month is not a well defined unit. There are four different month units: 28, 29, 30, 31 days. If you do calculations respecting this there will be no problem.

Complex numbers and vectors are not a good analogy.

I believe you fail to grok GP's point.

The mapping from "month" units to "days" units is well-defined. It's just some function that happens to be non-constant over the month ordinal.

Heck, if we start down the "precise time definition" rabbit hole, though, then neither does "day" have some philosophically unassailable notion, cf. leap seconds. Even the unit of "second" ends up pulling in a whole heck of a lot of physics machinery just to nail down some semblance of rigor.

Anyway, despite being such an intuitively simple and practically functional concept, the notion of time amd time measurement turns out to be suprisingly subtle and to have a fascinating history. I highly recommend jumping down that rabbit hole. Hehe

Anyway, I'm surprised calc.exe doesn't calculate with and store dates using some kind of epoch time.

Uh...October, November, December and Undecimber? I don't see the last one very often. (In other words, once you start delving into it, turns out it's neither simple nor unambiguous - which calendar? If Gregorian, when did the switch from Julian happen? Etc etc.)

I'm guessing he means that there's two distinct Februaries, in the gregorian calendar.

Yeah, that's also a possibility.

Months should not be used to describe durations. The length of one month is not clearly defined.

Edit: except when giving an approximation, which the program doesn't

And yet people do.

Because approximate durations suffice for casual conversation. When you are talking about the exact date (as computers do) you need something more accurate.

This example isn't about computer communication, it's about John Doe using the windows calculator to find the time between two dates.

Does that matter?

It is clear that the duration between 2015-05-15 [1] and 2019-06-19 is about 2019 - 2015 = 4 years or exactly 1,496 days (or 1,497 days including both endpoints, yeah this is also somewhat ambiguous). It is much less clear that the duration in question is 4 years, 1 month and 4 days; depending on the use case months can be uniformly 30 days long, may or may count the "excess" days, or even do not matter (in which case the duration would be 4 years and 35 days). Generally such a duration is ambiguous without a context and it would be misleading to present it as exact.

[1] https://github.com/rust-lang/rust/blob/master/RELEASES.md#ve... (in case you wonder)

I'm not really sure what you're getting it.

June 15th 2015 and July 19th 2019 is 4 years 1 month and 4 days apart. That is the duration that occurred between the two dates and is totally unambiguous to non-programmers.

The fact that calendar distance and 'time' distance don't have a linear relationship and that math that governs the relationship between the different units isn't straightforward doesn't make it unprecise or misleading.

It's totally exact. Just because you can't convert the calendar distance to time distance without context doesn't make it any less exact. Not so different to being unable to compute distance traveled by the revolution count of your wheels.

If you are doing the financial calculation or similar, the month unit is absolutely inaccurate and you will keep asking about edge cases (otherwise you may lose money). And if you don't do that, you can cope with approximate units like months, half-months or so. There is no reason that the exact calculation of duration should be done in approximate units.

Once again, we're talking windows built-in calculator. I really hope the specification is "make it output something that the average user will understand and agree with". And the operative keyword here would be "average".

I'm saying that the Windows Calculator does disservice to the general public by showing the exact-ish year-month-day duration at all, it's like experts oversimplifying the complex situation and making laypersons more confused. Sorry if this was not clear.

Any time you're showing more precision than your inputs or calculations can provide, you're lying to your users.

But the way people use it is not for exact dates and deltas. If today is March and I know something will happen in August, I'll say "this will happen in 5 months from now", but I've never heard anyone add days and weeks to such a duration. If today is 31st o'March and you tell me "next month", that could mean anything between one or thirty days, i.e. the difference between 31st o'March and 1st o'April is one month.

Edit: Also the same way that "tomorrow" can be in 5 minutes.

An month makes sense as an absolute value, not as a relative one.

The Windows calculator is terrible design. To calculate deltas between two dates you need to convert them to seconds, subtract them, and convert the result back to an absolute time.

But then you get the (in some ways correct) result that the difference between the 15th of one month and the 15th of another month is a couple months and couple of days, instead of the intuitive whole number of months.

That gives you an absolute time result, not a practical one. Using that methodology the calculator wouldn't be able to tell whether something is in one month or not.

How long is a month? How long is a year? How long is a minute? How are you going to do your subtraction?

I think most normal people (non programmers) would be surprised by how complex such seemingly simple types like real numbers, dates and text can become.

Interestingly enough Wolfram Alpha uses month to mean 30 days and 10 hours(rounded to 30.42 days when displayed) which is 365/12.

`1 month - 10 hours` = 30 days

`2 months - 20 hours` = 60 days

Every problem has a solution that is neat, simple, and wrong.

Never use an unsigned type for numbers.


After reading it, I think it is an acceptable bug. Just can’t test it.

May be the strategy when doing this is change to a different approach. Not calc as in unix or try hard to do it. But to it as a separate rule based program.

OMG why does this get repeated constantly. Microsoft released the source code to Windows Calculator, a Windows Store App. They did not release the source code to the venerable calc.exe

I think the software referenced in the article is the only calculator you get on Windows 10, and the binary is named calc.exe so you're technically correct and incorrect at the same time

No, it's named something more like "Calculator.exe", but calc.exe is a stub that will just activate the UWP app if present so that people and software can still invoke calc.exe.

The old calculator was briefly shipped as a separate component called "win32calc.exe" in 10-based versions without apps, and that's what the calc.exe stub defers to in those cases.

It's not important, but you're definitely wrong here.

The math engine is the same, just the UI layer is different

You said calc.exe and I thought you meant the one that’s closed source. I imagined you using IDA to find and fix the bug. This is definitely not as cool.

Also, props for working for free for a multibillion corporation.

> Also, props for working for free for a multibillion corporation.

Sure, because no one should be proud of fixing a bug for the millions of people that use that operating system. Satisfying intellectual curiosity and helping somebody for free, the big evils of our time!

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