Work the Shell - More Fun with Days and Dates

Figuring out how to calculate the year for a given date and day of week is a task that's not as easy as it sounds.
Could the Date Occur in the Current Year?

The next set of tests is one I rewrote a couple times to ensure that I wasn't tripping myself up, because my first thought simply was to use a test like this:

if [ $month -le $thismonth -a $day -le $thisday ]

But, then I realized that in edge cases it wouldn't actually work properly. For example, let's say it's April 4 and you're checking for March 11. The month test succeeds, but the day test fails—not what we want. Instead, let's use a cascading set of conditional tests:

if [ $monthnum -gt $thismonth ] ; then
  # month is in the future, can't be this year
  mostrecent=$(( $thisyear - 1 ))
elif [ $monthnum -eq $thismonth -a $day -gt $thisday ] ; then
  # right month, but seeking a date in the future
  mostrecent=$(( $thisyear - 1 ))

With just this much code, we can at least test the normalization of data input and comparison tool. I ran this set of tests on March 1, by the way:

$ Monday Aug 3
Decided that for 8/3 we're looking at year 2010
$ sh mon jan 9
Decided that for 1/9 we're looking at year 2011
$ mon mar 1
Decided that for 3/1 we're looking at year 2011
$ mon mar 2
Decided that for 3/2 we're looking at year 2010

It correctly identified that the current date could be a match, but that the subsequent day (mar 2) had to be in the previous year for it to be a possibility.

Good. Next month, we'll put the rest of the LEGO pieces in the model and have a working script. The big task left? Parsing the output of cal to figure out the day of the week for a given date.

Dave Taylor has been hacking shell scripts for a really long time, 30 years. He's the author of the popular Wicked Cool Shell Scripts and can be found on Twitter as @DaveTaylor and more generally at


Dave Taylor has been hacking shell scripts for over thirty years. Really. He's the author of the popular "Wicked Cool Shell Scripts" and can be found on Twitter as @DaveTaylor and more generally at


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

GNU `date' is enough...

zeroxia's picture

As we are talking about Linux, the GNU `date' utility could be far more clever than you might thought, so there is no need to mess with the output of `cal'...

Quick hint:
date -d "Sep 25 2008" +%A
date -d "Sep 25 2008" +%u

But this probably is GNU only, AFAIK, at least the BSD `date' does not have such magic.

Here is an `sh' script listing I just crafted:


export LC_TIME=C

    cat <<!
    ${0##*/} WEEKDAY MONTH_NAME Day
    ${0##*/} WEEKDAY MONTH-DAY
    ${0##*/} WEEKDAY MONTH/DAY

if [ $# -ne 3 -a $# -ne 2 ]; then
    exit 1
if [ $# -eq 3 ]; then
    # GNU `date' accepts "Sep 25 2008"
    fmt="$2 $3 %d"
    # And also accepts "2011-9-25" or "9/25/2011"
    case "$2" in
            echo "Uknown date: $2"
            exit 1

case $(echo $1 | tr '[:upper:]' '[:lower:]' | cut -c1-3) in
    mon) weekday=1 ;;
    tue) weekday=2 ;;
    wed) weekday=3 ;;
    thu) weekday=4 ;;
    fri) weekday=5 ;;
    sat) weekday=6 ;;
    sun) weekday=7 ;;
        echo "$1: Unknown weekday"
        exit 1



y0=$(date +%Y)
while [ $i -lt $MAX_TRY ]; do
    y=$((y0 - i))
    str=$(printf "$fmt" $y)
    if ! j=$(date -d "$str" +%u); then
        # `date' will complain, so I keep quiet
        exit 1
    if [ $j -eq $weekday ]; then
        echo $(date -d "$str" +$MY_DATE_FMT) is \
            $(date -d "$str" +$MY_WDAY_FMT)
    i=$((i + 1))

test $found -eq 0 && exit 1
exit 0