|
These programs were featured online at UnixReview.com, Shell Corner, September 2003. |
||||||||||||
|
http://www.OrlandoKuntao.com
|
||||||||||||
|
Perl and AWK Holiday Determination Routines
by Bob Orlando |
||||||||||||
|
Shortcuts to the code (skip the descriptions for now
The following describes both Perl and AWK scripts developed for determining whether a given date is a holiday, or the nth business day of the month. These routines are based on holiday algorithms from Marcos J. Montes ("American Secular Holidays") and Claus Tondering ("Frequently Asked Questions about Calendars"). Holiday Scheduling American Secular Holidays in Perl Initially, I coded the algorithms in a Perl program function or subroutine, &holiday_date (in today_is.pl). Later I added another function, &load_holiday_array, to pull the necessary information from a holidays file. Lastly, I put together a third function, &business_day, to return the nth business day's date (in the form yyyymmdd) for a given year and month (yyyymm). All three functions (called in today_is.pl) were subsequently rolled up into a single Perl macro, Holiday_dates.html (Business_day, which calls holiday_dates, has proven to be as useful as the holiday routine itself for those processes that must run, for example, on the second business day of the month.) American Secular Holidays in AWK Calculating Business Days
bizday=`nawk -f holidays.awk -- -b 2 holidays`
if [ `date "+%Y%m%d"` = $bizday ]; then
echo "Today is the 2nd business day of the month."
# Do whatever
fi
Last business day and business day offset from the last business day (negative numbers) is also available in holidays.awk. To retrieve the last business day of the month, specify the "last" option argument (optarg) for -b option (i.e., "-b last"). For the next-to-last business day of the month, provide "-b -1" as an option and optarg.
Holidays.awk is a well-behaved program in that it uses exit status to indicate success or failure. As indicated in the documentation, all options except business day (-b), returning a zero status means the program completed successfully; non-zero indicates failure. However, with the business day option, non-zero indicates success because it is the day of the month on which the business day falls. Therefore, use the holidays.awk the exit status as the test comparand:
nawk -f holidays.awk -- -b last holidays > /dev/null 2>&1
if [ $? -eq `date +%d` ]; then
echo "Today is the last business day of the month."
# Do whatever
fi
You can also combine -b with -m and -y to return the nth business day for a given month and year. If you request a business day (positive or negative) that is not found in the month, you receive an error message, and a 0 exit status indicating an error.
For those needing only an indication that today is a given business day, you can use the -t option in conjunction with -b. For example, using Unix cron (scheduler) we combine those options to set up a job to run only on the second business day of the month with as little as the following:
00 02 2-5 * * /usr/local/bin/holidays.awk -- -b 2 -t \
|| some_program > some_program.out 2>&1
In this example, no holidays file is specified because we use the default, /usr/local/bin/holidays (you can change the program to point to wherever you wish to locate the file). No nawk -f is used because the first line of holidays.awk uses the shebang syntax (#!/usr/bin/nawk -f) to execute itself. (Obviously, the program must have the necessary execution permissions to run this way.) With the -t option, holidays.awk returns true or false (which is not the same as success or failure), only running the called program if the day is, indeed, the second business day of the month.
Returning The Nth Weekday (Awk Method)
fst_monday=`nawk -f holidays.awk -- -d 1.Mon`
An alternative syntax is also provided: nawk -f holidays.awk -- -d 1.1
You can expand this to report the first Monday in any month and year like this.
yyyy=2005
for mm in 1 2 3 4 5 6 7 8 9 10 11 12
do
nawk -f holidays.awk -- -y $yyyy -m $mm -d 1.1
done
For the last Sunday in a month use
nawk -f holidays.awk -- -d last.Sun
For those preferring a simpler syntax: If your OS recognizes the #! (shebang) syntax, you can place a #!/usr/bin/nawk -f (or gawk) at the start of holidays.awk, thereby allowing you skip the [gn]awk -f during invocation and simply call it like this,
holidays.awk -- -d last.Sun
holidays.awk -- -d last.0
holidays.awk -- -d 5.0
The Perl Method
Nth day capability exists in today_is.pl, (see fst_monday in today_is.pl), but essentially, for the last Friday in May, call holiday_yyyymmdd subroutine like this:
$last_friday = holiday_yyyymmdd(200405,"last",6);
$last_sunday = holiday_yyyymmdd(200502, 5,0);
$second_mon = holiday_yyyymmdd(200502, 2,1);
Testing with Holidays.sh Holidays.sh executes holidays.awk, providing examples of holiday and business day testing. Provided the holidays file is located properly, executing holidays.sh on June 21, 2003 displays:
Today's no holiday, get busy. :-((
20030101 Wed. New Year's Day
20030120 Mon. M.L.King Jr. Birthday
20030526 Mon. Memorial Day
20030704 Fri. Independence Day
20030901 Mon. Labor Day
20031127 Thu. Thanksgiving Day (US)
20031128 Fri. Thanksgiving Day II (US)
20031225 Thu. Christmas Day
Today is NOT the 2nd business day (20030603) of the month.
Today is NOT the last business day (20030630) of the month.
Today is NOT the next-to-the-last business day (20030627) of the month.
As a real acid test, I include the next-to-last and last business days of every month from 2000 to 2010. The holidays.sh script concludes with a report for all holidays for the 21st century.
The Holidays File After a brief description of holidays file layout, I'll discuss the the file itself, and see how three holidays are handled: Memorial Day, Thanksgiving Day (including the Friday after), and Christmas. The file itself is a simple ASCII file available to to all programs. It contains values that allow the calling program to calculate holidays either by given (fixed) month and day, or by day of a given week. The general layout is as follows:
# Mm N.Day Adj Holiday name # Comments
Mm = Month number (leading zeros NOT required)
N.day = Nth day (1-5 and "last") "." weekday (0-6)
(Not every part is required.)
Adj = Can be either a +|- n days,
or weekday followed immediately by a +|- n days,
Holiday name = How you want it spelled out--your call.
Comments = ignored.
Leading white space is ignored, as is everything following and including the octothorpe (#-sign). Here are the entries for the three holidays:
#-----------------------------------------------------------------------#
# Mm N.Day.OnOrA Adj Holiday name # Comments #
# -- ----------- --- -------------------------------- ----------------- #
05 last.1 Memorial Day # Last Mon in May
11 4.4 Thanksgiving Day (US) # 4th Thu in Nov
11 4.4 +1 Thanksgiving Day II (US)
12 25 Christmas Day # M-F
12 25 6-1 Christmas Day (pre-holiday obs) # Sat? Use Fri
12 25 0+1 Christmas Day (post-holiday obs) # Sun? Use Mon
Memorial Day Memorial Day is the last Monday in May. In the table the month is "05" (again, leading zero is unnecessary). The last Monday is specified by the word "last" and not a 5 because the last Monday may not be the 5th Monday (there is no 5th Monday in May, 2003). Monday is identified by the 1 following the dot (".1"). This is based on the 0-6 convention for representing Sunday through Saturday. Thanksgiving Day Friday After Thanksgiving [Thurs]Day
11 4.5 Thanksgiving Day II (Friday)
since the fourth Friday might not follow the fourth Thursday of a given month. Consider Thanksgiving Day, 2002--the fourth Thursday was November 28. The fourth Friday fell on the 22nd. So, to accurately capture the Friday after Thanksgiving Day, specify the same parameters for Thanksgiving, and an adjustment of +1:
11 4.5 +1 Thanksgiving Day II (Friday)
Christmas Christmas is December 25. Like New Year's Day (January 1) and Independence Day (July 4), Christmas is a fixed date. Simply specifying "12 25 Christmas Day" in the holidays file returns "yyyy1225". However, with many companies, if Christmas falls on a Saturday (day 6), the Friday before is observed by adjusting it by -1. If it falls on a Sunday (day 0), the Monday following is observed by adjusting it by +1. Hence, the three entries:
12 25 Christmas Day # M-F
12 25 6-1 Christmas Day (pre-holiday obs) # Sat? Use Fri
12 25 0+1 Christmas Day (post-holiday obs) # Sun? Use Mon
New Year's Day New Year's Day is a fixed date, January 1, and like Christmas and Independence Day, it can be observed on the Friday before a Saturday occurrence or the Monday after a Sunday occurrence simply by setting it up like the Christmas example above. However, some organizations use a post-holiday observance of New Year's Day when it falls on a Saturday simply so the holiday falls in the correct year. You can do that by specifying New Year's Day as follows:
01 01 New Year's Day # M-F
01 01 6+2 New Year's Day (post-holiday obs) # Sat? Use Mon
01 01 0+1 New Year's Day (post-holiday obs) # Sun? Also Mon
Remember, the "6" in our "6+2" means the actual date, January 1st, falls on a Saturday (day 6 in the 0-6 day-numbering schema), so adjust that date by +2 days (i.e. Saturday's date (01/01) plus two days (01/03).
Daylight Savings Time
04 1.0.6 Falklands ST # 1st Sun on/after Apr 6
09 1.0.8 Falklands DST # 1st Sun on/after Sep 8
The ".6" in our "1.0.6" means Standard Time (ST) begins on the first Sunday (1.0) in April that falls on or after the 6th of April. Likewise, the ".8" in our "1.0.8" means DST begins on the first Sunday in September that comes on or after the 8th of September. Since Daylight Savings dates are not usually holidays, you can also retrieve the Daylight Savings Time dates via the -d option and bypass the need for the holidays file altogether. Here are Daylight Savings Times for the United States (begins the second Sunday in March) and the Faulklands (begins on the first Sunday on/after September 8).
holidays.awk -- -d 2.0 -m 3
holidays.awk -- -d 1.0.8 -m 9
You can even set up a cron to test for Daylight Savings Time and perform some action if true.
05 00 * 03 * [ `/usr/local/bin/holidays.awk -- -d 2.0 -m 3 -t` -eq 1 ] \
&& ... Some action ...
Conclusion |
|
©Copyright Bob Orlando, 2003-2016 All rights reserved. |
http://www.OrlandoKuntao.com E-mail: Bob@OrlandoKuntao.com |
Last update:
Feb. 2, 2016 by Bob Orlando |