My Y2020 Bug
For reasons that must have been clear at the time, I once wrote a test in terms of epoch time, and wanted it to run on systems that did not use January 1 1970 as the epoch. So I loaded Time::Local and added
timegm( 0, 0, 0, 1, 0, 70 ) to the desired epoch.
This morning I got a CPAN testers report failure. It seems that if you give
timegm() a year in the range
0-99 it assumes it is within 50 years of the current year, so my test suddenly thought the epoch was 2070.
In this case, the obvious response is to specify a four-digit year.
Maybe a better response is to ditch
timelocal() completely in favor of
timelocal_modern(). These require
1.27, released June 2019. According to its metadata it works back to Perl 5.6, though so far I have only verified it back to Perl 5.8.1.
With thanks to Dave Rolsky for the
*_modern() variants, and to Chris Williams (BINGOS), who uncovered this in one of his CPAN tester systems.
CPAN Testers Rule!
Is that in fact a better response?
It comes at the cost of an additional dependency that old perls will have to install from CPAN, and every dependency installed from CPAN has the potential to come at the cost of having to upgrade your any amount of your toolchain.
Of course, this is always the case for dependencies, and it doesn’t mean that dependencies are bad per se. We add them because they provide value. All it means is that dependencies also incur a cost, meaning there is a trade-off.
So then what is the benefit to adding this dependency?
Well, if you use the _modern variants, you have to write the year as 1970.
Just like you have to if you do not.
Meaning, your use case gains absolutely nothing from the added dependency. Zero. All the added dependency does is make your code slower to install on older perls, in exchange for… the empty set.
So far my personal problem seems limited to a test where I specified a two-digit year. That is now corrected, and that may be all I do.
However, had the code originally been written with the then-unavailable
*_modernvariants, this bug would have been caught as soon as it was tested, rather than a decade or so later when it actually manifested.
I have great faith in my own ability to write buggy code, and tend to program defensively as a result. Using the modern variants strikes me as defensive programming. Not using them strikes me as assuming that I will always use a weird interface correctly, and that I have never and will never write this bug again.
Time::Piece->strptime( '1970-01-01', '%Y-%m-%d' )far more defensive in that sense than either
timegm( 0, 0, 0, 1, 0, 1970 )or
timegm_modern( 0, 0, 0, 1, 0, 1970 )?
(Isn’t it great that that says
… 1, 0, 1970rather than
… 1, 1, 1970? Do you always immediately remember which one is supposed to be zero and which one is 1 and why?)
Before I respond to your comment above, thank you very much for Modern functions in a post-modern language. If only we had a core
timegm()that round-tripped with
strptime()does not go back before 1900. The module in question is about satellite positions, and my correspondence says it is in use professionally (which is why I am a bit paranoid about accuracy) and
strptime()would suffice for that.
But the support modules have taken on a life of their own, and I have gotten correspondence about sunrise/sunset calculations as far back as 3000 BC. I have no way to assess the accuracy of my calculations that far back, and say so. But I would hate to close out such uses, and other modules I have published use algorithms whose accuracy that far back is known.
You provided the impetus to write it. 🙂 There had been other discussions about the new functions, but the conversation in this comment thread is what pushed me to consolidate my thoughts into an article – both so I wouldn’t have to repeat myself, but more importantly to have the scope to lay out the full case, since individual points by themselves are not very persuasive.
No surprise… arguably exactly what happened with Time::Local. I understand your position, I ran into similar issues trying to figure out how to fix the 2020 bug in Date::Parse – which advertises the fact that it’s built on top of Time::Local. (There are lots of patches claiming to fix that module, none of which seem correct to me on closer examination.)
Or, one daren’t even think it, something more reasonable than crusty old