Yet Another Friday the 13th
I'm an avid horror fan, big surprise! I like horror movies of all types: zombies, slasher, B-grade, C-grade, gore and even oldschool thriller horror movies like Hitchcock. To this day, I host a Friday the 13th event at my house every time for my friends and I. We run a marathon of as many movies as we can. Sometimes we make it through two, sometimes five. It's not always easy to stay up! :)
These past few months have been pretty difficult and busy. At 10pm I got a message from a friend in Canada: "happy Friday the 13th!" - Shit! I missed one! Well, no matter. The question is: how do I make sure not to miss one again? This is something I considered more than once and was always too lazy to actually try out.
So, I jotted down this code to accomplish it. I'd be happy to hear of better ways you can come up with:
I got the following output:
Friday the 13th on: 13/04/2012
Friday the 13th on: 13/07/2012
Friday the 13th on: 13/09/2013
Friday the 13th on: 13/12/2013
Friday the 13th on: 13/06/2014
Friday the 13th on: 13/02/2015
Friday the 13th on: 13/03/2015
Friday the 13th on: 13/11/2015
Friday the 13th on: 13/05/2016
Friday the 13th on: 13/01/2017
It seems as though I missed the last Friday the 13th this year, but there are two next year and it seems like 2014 should be a big event because there's just one! 2015 has three!
Meanwhile, maybe I should go for a one-time Saturday the 14th?
Here's my solution:
Nice! Much shorter!
I don't know how accurate Date::Calc is, though, so I'd probably feel safer with DateTime. I might be wrong on this though.
Still, really kickass solution there! :)
Here's my improved version:
foreach my $year ( 2012 .. 2020 ) {
foreach my $month ( 1 .. 12 ) {
my $dt = DateTime->new(
day => 13,
month => $month,
year => $year
);
if ( $dt->day_name eq 'Friday' ) {
print $dt->dmy('/'), "\n";
}
}
}
I've used Date::Calc in production code at two different employers, and never had a problem with it. I've always felt it to be rock solid, and it has copious documentation, which is usually a good indicator.
I like its whipupitude as well, given the large number of utility functions.
Also take notice of Date::Calc's motto: "Keep it small, fast and simple". Try holding 10k or 100k DateTime objects in memory and see your process balloon (of course, not everyday you'll need to do that).
Sawyer! How could we missed Friday the 13th again?
Liked your thought about this little script, but can you add a way that it will add a reminder to your calendar? That way we really won't forget about Friday the 13th (:
My concern was for accuracy, not size. My understanding of dates is very little, but I know DateTime makes additional releases for stuff like leap seconds.
Thanks Aristotle!
Would you mind if I include this in the gist repo? Or use it elsewhere?
Neil, would you mind if I include your solution in the gist repo, or use it elsewhere?
Not at all, go ahead.
How exactly do you expect leap seconds to make a difference as to whether a particular Friday falls on the 13th?
Leap years, certainly, but I can't imagine that any unexpected additional leap years will be proclaimed in my lifetime.
I wasn't thinking leap seconds will change that. I was trying to state that I don't know much about dates and that perhaps there is something that requires using one module over the other for accuracy.
I guess that was wrong.
Here's a Perl 6 one-liner: http://irclog.perlgeek.de/perl6/2012-07-18#i_5825055
Thank you for adding in.
I also saw sorear's Bash example, and the comment he has made, and I intend to write a follow up post on that. Using two compiled programs to use it and then claim it was longer to do it in one... unfair. :)
That Perl 6 one-liner is, of course, the 6ification of the approach I took. :-)
It is notable insofar as it is pretty much a no-frills direct transcription of how I thought of the solution I tried to code. Perl 5 does not provide sufficiently high-level primitives to express the same intent directly.
Now I wonder what a corresponding transcription in Haskell would look like.
I cannot think of many other languages that would make this easy in a similar way. Probably APL would, but, err.
SQL does unexpectedly well on this job! Of course this is exactly the sort of set operation that SQL is designed for, so it is not that unexpected, but I was still surprised at how straightforward it is to say this in SQL… except for the surprise lack of expressiveness that makes it all go badly wrong. The surprise is where it falls down: there is no concise way to generate a range like
2012..2020
or1..12
. Here it is in MySQL dialect:If you disregard the huge copypasta necessary to supply the source data tables inline, the query itself is expressed almost directly. (Does Postgres manage to one-up the other databases on this? I hope so. Anyone? David?)
The only trick is the use of
GROUP BY
to be able to useHAVING
instead ofWHERE
, becauseWHERE
cannot refer to values computed inside theSELECT
clause whereasHAVING
can. One could use a subselect instead to defer theWHERE
by another layer, but that would be more verbose.So imagine prepopulated
month
andyear
tables, then it could be written like this:That is almost novice-level SQL. If a novice can’t write this, that same novice will at least almost certainly be able to follow it.
Beautiful!
I've opened a new repository strictly for this, and I will upload your SQL solution tonight as well! :)
Actually, scratch the
GROUP BY
clauses. They are unnecessary. AHAVING
clause can stand by itself. I.e. this is the short form of the query: