In a lot of legacy code, I've come across "integer dates". It's a pretty common way to store dates in a compact format: an integer in the form "YYYYMMDD", e.g., 20200616 It's relatively compact, it remains human readable (unlike a Unix epoch). It's not too difficult to play with the modulus and rounding operators to pick it back into date parts, if you need to, though mostly we'd use something like this as an ID-like value, or for sorting.

Thanks to Katie E I've learned about a new format: decimal years. The integer portion is the year, and the decimal portion is how far through that year you are, e.g. 2020.4547. This is frequently used in statistical modeling to manage time-series data. Once again, it's not meant to be parsed back into an actual date, but if you're careful, you can do it.

Unless you're whoever wrote this C++ code, which Katie found.


*unYear = (unsigned short)m_fEpoch;
*unMonth = (unsigned short)(((m_fEpoch - (float)*unYear) * 365.0) / 12.0) + 1;

Right off the bat, we can see that they're using pointers to these values: *unYear tells us that unYear must be a pointer. This isn't wrong, but it's a code smell. I've got to wonder why they're doing that. It's not wrong, it just makes me suspicious.

The goal, as you can see from the variable names, is to figure out which month we're in. So the first step is to remove the year portion- (unsigned short)m_fEpoch will truncate the value to just the year, which means the next expression gets us the progress through the year, the decimal portion: (m_fEpoch - (float)*unYear).

So far, so good. Then we make our first mistake: we multiply by 365. So, on leap years, you'll sometimes be a day off. Still, that gives us the day of the year, give or take a bit. And then we make our second mistake: we divide by 12. That'd be great if every month were the same length, but they're not.

Except wait, no, that wouldn't be great, because we've just gotten our divisors backwards. 365/12 gives us 30.416667. We're not dividing the year into twelve equally sized months, we're dividing it into thirty equally sized months.

I've seen a lot of bad date handling code, and it's so rare to see something I've never seen before, an interesting new way to mess up dates. This block manages to fail to do its job in a surprising number of ways.

In any case, summer approaches, so I hope everyone enjoys being nearly through the month of Tredecimber. Only 17 more months to go before 2020 is finally over.

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!