Unix Tip: Last workday of the month

By Sandra Henry-Stocker, ITworld.com |  Hardware 1 comment

Send in your Unix questions today! |
See additional Unix tips and tricks


We looked at various ways to write scripts that would only run on the last day of the month in the column "Is this the last day of the month?". Determining whether an arbitrary day is the last day of the month can be done in a number of ways, using the cal and/or the date command. For example, if tomorrow is the first, then today must be the last day of the month. This gets us around the complexity of months with 28, 30 or 31 days.


When a reader recently asked how to write a script that would only run on the last workday of the month, a new twist was added to the old problem. A day is the last workday of a month only if it's a weekday and the following weekday falls in the next calender month. So, I came up with two approaches to solving the new challenge.



The most obvious solution is to use the human-friendly output of the cal command. Here's the output from the "cal" command for this month:

   January 2007
 S  M Tu  W Th  F  S
    1  2  3  4  5  6
 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

We can clearly see that Wednesday, the 31st is the last workday in January 2007. To calculate this in a script, we can use a clunky, but very straightforward command such as this:

cal | grep -v "^$" | tail -1

This would give us the last non-empty line in the cal output. We still need to pick out the last workday. If the last week in the month looked like this (as it will this June), we don't want to select the last date on the list as this would be a Saturday:

24 25 26 27 28 29 30

Instead, we would want to select the 29th -- the next-to-last date on the line. We can do this by counting the dates in the output and selecting the last date on the line only if there are fewer than seven dates.

#!/bin/bash

caldata=`cal | grep -v "^$" | tail -1`

numdays=`echo $caldata | awk '{print NF}'`

if [ $numdays -le 6 ]; then
    echo $caldata | awk '{print $NF}'
else
    echo $caldata | awk '{print $6}'
fi

A somewhat more elegant, though still highly readable, approach is to use the date command with time zone arguments. In this approach, we can determine that a day is the last workday of the month if it's a Monday, Tuesday, Wednesday or Thursday and the following day is the first or if it's a Friday and the following Sunday is either the first or the second.

#!/bin/bash

timezone=`date +%Z`                             # time zone
DoW=`date +%w`                                  # day of week

tomorrow=`TZ=$timezone-24 date +%d`             # today's date (e.g., 27)
dayafter=`TZ=$timezone-48 date +%d`             # day after tomorrow

case $DoW in
[1-4]) if [ $tomorrow == 1 ]; then
        lastworkday=true
        fi;;
[5])   if [ $dayafter -le 2 ]; then
        lastworkday=true
        fi;;
esac

if [ $lastworkday ]; then
    echo your code here
fi

The "TZ=$timezone-24 date +%d" and "TZ=$timezone-48 date +%d" commands look a little odd, but they represent commands such as these:

TZ=EST date +%d
TZ=EST-24 date +%d

Try this using your own time zone and you should get the calendar dates for today and tomorrow. Tomorrow's date is calculated by offsetting today's date by 24 hours and the day after by 48.



There are probably some excessively clever one-liners out there that can also be used to pick out the last workday of the month, though I suspect they're as dense as they are brilliant. If you have crafted one of these wonders, please share it.

 

1 comment

    Anonymous 3 years ago
    For your first snippet, what if the last day of the month falls on Sunday?

      Add a comment

      Post a comment using one of these accounts
      Or join now
      At least 6 characters

      Note: Comment will appear soon after you have activated your account.
      Obscene/spam comments will be removed and accounts suspended.
      The information you submit is subject to our Privacy Policy and Terms of Service.

      ITworld LIVE

      Ask a question

      Ask a Question