Calculating the second last Friday of the month

My boss, bless ‘im (no really, do; he’s a sound bloke, great guy to work for, and is just getting through some serious health problems), needs a monthly status report on the second last Friday of every month. I live by my calendar applications reminding me to do things, so I thought it’d be no problem getting Outlook to set up a reminder.

No dice; it will only set up appointments on the 1st, 2nd, 3rd etc., starting from the beginning of the month. I did a web search, and really thought I’d found a solution for iCal. It was not to be; this was for a Unix program called ICal; dratted case-insensitive search. Curiously, it appears that the ics spec might support a second-from-last syntax, but Outlook and iCal (and Google Calendar) can’t create them. Phooey.

So I tried excel; and really thought I’d found the basis of an answer: Last Friday of the month. And indeed, most of their assumptions are right; the code

DATE(year,month+1,1)-WEEKDAY(DATE(year,month+1,1),1)

really does give you the date of the last Saturday in the month. But you can’t assume that the day before the last Saturday is the last Friday – it is the second last, if the month ends on a Friday (April 2010 is a test case).

So I tried the Swiss Army chainsaw of brute-force date calculation: Perl with Date::Calc. What I do here is create an array of every Friday in the month, then print the second last member; never known to fail:

#!/usr/bin/perl -w
# second_last_friday.pl - show date of 2nd last friday
use strict;
use Date::Calc qw(Today
  Nth_Weekday_of_Month_Year
  Add_Delta_YMD);
my ( $new_year, $new_month ) = 0;

my ( $year, $month, $day ) = Today;
foreach ( 1 .. 24 ) {
    my @fridays = ();   # for every friday in this month
    foreach my $week ( 1 .. 5 ) {
        if (
            ( $new_year, $new_month, $day ) =
            Nth_Weekday_of_Month_Year(
                $year, $month, 5, $week
            )
          )
        {               # day of week 5 is Friday
            push @fridays, $day;
        }
        else {
            last;       # not a valid Friday
        }
    }
    printf( "%4d/%02d/%02d\n",
        $year, $month, $fridays[-2] );
    ( $year, $month, $day ) =
      Add_Delta_YMD( $year, $month, 1, 0, 1, 0 )
      ;                 # month++
}
exit;

and this gives


2009/11/20
2009/12/18
2010/01/22
2010/02/19
2010/03/19
2010/04/23
2010/05/21

...

See, notice the tricksy 23 April 2010, which – considering thirty days hath April et al – ends on a Friday and threw that simple Excel calculation off.

I’m disappointed that all these new applications like Outlook and iCal don’t seem to handle dates as elegantly as the old unix programs I used to use. pcal, in particular, could generate incredibly complex date formulae. I must dig around to solve this problem – and for now, actually have to remember to write that report on the second last Friday of this month …

5 comments

  1. Wouldn’t DATE(B12,C12+1,1)-IF(WEEKDAY(DATE(B12,C12+1,1),1) > 6, 0, WEEKDAY(DATE(B12,C12+1,1),1))-8 do the trick in Excel?

  2. How about doing it by hand ?.

    2 years worth is only 24 manual entries, and you could put a reminder on the 23rd one to create 24 more.

  3. Why spend 10 minutes every two years writing entries manually when you can spend all morning writing a program to do it?

Leave a comment

Your email address will not be published. Required fields are marked *