This post is part of a series on Mohammad Anwar’s excellent Weekly Challenge, where hackers submit solutions in Perl, Raku, or any other language, to two different challenges every week. (It’s a lot of fun, if you’re into that sort of thing.)
Task #2 this week asks us to (and I quote): print all Palindrome Dates between 2000 and 2999. The format of date is mmddyyyy. For example, the first one was on October 2, 2001 as it is represented as 10022001.
It’s pretty easy to avoid using any sort of date library with a couple of key observations about the problem domain. First, we’ll split it up into month (mm
), day (dd
), century (cc
), and 2-digit year (yy
). Thus our “baseline” date format is mm dd cc yy
.
If we use R[xx]
as shorthand for “string reverse of xx“, we can rewrite the date in a couple of different ways:
mm dd cc yy # Original
R[yy] R[cc] cc yy # Start with year
mm dd R[dd] R[mm] # Start with mm/dd
R[yy] dd R[dd] yy # Start with yy and dd
I didn’t list all of the possible combinations. The last one is the one I’m interested in, because:
Since the month is the reverse of the year, and months range from 01..12, that means yy
must be limited to 01..12, each reversed (10, 20, 30, … 11, 21).
Since the century is the reverse of the day, and the century varies from 20..29, there are only three possible days (dd
) that work: 02, 12, and 22.
Furthermore, we don’t need to check for days in a month, leap years, or any of that Julian nonsense, because all of our dd
s are less than the shortest possible month length of 28 days. (That’s also why we don’t need a date library.)
Thus, instead of checking every single date for a thousand years, we only need to iterate a total of 12 * 3 = 36 times, and this is the optimal number, meaning each time we loop, we can print a valid palindromic date. And, the way I’ve chosen to loop, the results will already be in sorted order.
Raku
Raku makes things easy with the X
cross product operator, and the built-in ability to use n-at-a-time -> $dd, $yy
pointy blocks:
for (<02 12 22> X (1..12)».fmt('%02d')».flip.sort).flat -> $dd, $yy {
say "{$yy.flip}-$dd-{$dd.flip}$yy";
}
Perl
Perl isn’t far behind on expressiveness, though:
my @yy = sort map { chop . ($_||0) } 1..12;
for my $dd (qw<02 12 22>) {
for my $yy (@yy) {
printf "%02d-%02d-%02d%02d\n",
scalar reverse($yy), $dd, scalar reverse($dd), $yy;
}
}
The chop
is a bit of a cheeky way to go, here, to show another way of reversing a two-character string. Since I know all of the strings in that map
will be two characters, I’m just removing the last character and concatenating the remainder, which is another way of saying scalar reverse
. I could have also done it with arithmetic, substr
, or at least half a dozen other ways.
02-02-2020 / 2020-02-02
February 2nd this year (Groundhog Day, in some countries) was super-duper-extra special, as it was a palindromic date whether you interpret it as mm/dd/yyyy, dd/mm/yyyy, or even yyyy/mm/dd. The last one of these was quite a while ago.