Work the Shell - More Fun with Days and Dates

 in
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 ))
else
  mostrecent=$thisyear
fi

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:

$ whatyear.sh Monday Aug 3
Decided that for 8/3 we're looking at year 2010
$ sh whatyear.sh mon jan 9
Decided that for 1/9 we're looking at year 2011
$ whatyear.sh mon mar 1
Decided that for 3/1 we're looking at year 2011
$ whatyear.sh 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 www.DaveTaylorOnline.com.

______________________

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 www.DaveTaylorOnline.com.

Comments

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:

#!/bin/sh

export LC_TIME=C

usage()
{
    cat <<!
USAGE:
    ${0##*/} WEEKDAY MONTH_NAME Day
    ${0##*/} WEEKDAY MONTH-DAY
    ${0##*/} WEEKDAY MONTH/DAY
!
}

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

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
        ;;
esac

MY_DATE_FMT="%Y/%m/%d"
MY_WDAY_FMT="%A"

MAX_TRY=5

y0=$(date +%Y)
i=0
found=0
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
    fi
    if [ $j -eq $weekday ]; then
        echo $(date -d "$str" +$MY_DATE_FMT) is \
            $(date -d "$str" +$MY_WDAY_FMT)
        found=1
    fi
    i=$((i + 1))
done

test $found -eq 0 && exit 1
exit 0
White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState