This script is written based on the list of U.S. federal holidays I found in Wikipedia – Wikipedia – U.S. Federal holidays. Some of the dates, such as New Year, are straight forward, as the date and month are fixed. While others require some effort, take for instance Thanksgiving, which is on the fourth Thursday of November OR Memorial day – last Monday of May.

The script is written in bash, and only tested in 32 bit Ubuntu netbook. It will exit with an error message if you try to get the holidays for the year 2038 or above. This is a know issue with UNIX dates on 32 bit Operating Systems – UNIX: Year 2038 problem

Sample output

daniel@dnetbook:~$ /usr/local/bin/federalholidays.sh
Usage: federalholidays.sh Year
Eg. federalholidays.sh 2014


daniel@linubuvma:~$ ./federalholidays.sh 1500
New Year's Day:               Monday, January 01, 1500
Martin Luther King, Jr. Day:  Monday, January 15, 1500
Washington's Birthday:        Monday, February 19, 1500
Memorial Day:                 Monday, May 28, 1500
Independence Day:             Wednesday, July 04, 1500
Labor Day:                    Monday, September 03, 1500
Columbus Day:                 Monday, October 08, 1500
Veteran's Day:                Sunday, November 11, 1500
Thanksgiving:                 Thursday, November 22, 1500
Christmas Day:                Tuesday, December 25, 1500


daniel@linubuvma:~$ ./federalholidays.sh 2014
New Year's Day:               Wednesday, January 01, 2014
Martin Luther King, Jr. Day:  Monday, January 20, 2014
Washington's Birthday:        Monday, February 17, 2014
Memorial Day:                 Monday, May 26, 2014
Independence Day:             Friday, July 04, 2014
Labor Day:                    Monday, September 01, 2014
Columbus Day:                 Monday, October 13, 2014
Veteran's Day:                Tuesday, November 11, 2014
Thanksgiving:                 Thursday, November 27, 2014
Christmas Day:                Thursday, December 25, 2014


daniel@linubuvma:~$ ./federalholidays.sh 2500
New Year's Day:               Friday, January 01, 2500
Martin Luther King, Jr. Day:  Monday, January 18, 2500
Washington's Birthday:        Monday, February 15, 2500
Memorial Day:                 Monday, May 31, 2500
Independence Day:             Sunday, July 04, 2500
Labor Day:                    Monday, September 06, 2500
Columbus Day:                 Monday, October 11, 2500
Veteran's Day:                Thursday, November 11, 2500
Thanksgiving:                 Thursday, November 25, 2500
Christmas Day:                Saturday, December 25, 2500

Here is the whole script, feel free to modify it or report any problem –


#!/bin/bash

ARCH=$(arch)
ARGC=$#

function Usage
{

echo "Usage: $(basename $0) Year"
echo "Eg. $(basename $0) 2014"
exit 1

}

# we will need the year as argument in YYYY format
[[ $ARGC -ne 1 ]] &&  Usage

myyear="$1"
dformat='+%A, %B %d, %Y'

[[ "$myyear" -ge 2038 ]] && [[ "$ARCH" = "i686" ]] && echo 'Year 2038 problem : http://en.wikipedia.org/wiki/Year_2038_problem ' && exit 1

#We will ignore any year below 1902
[[ "$myyear" -lt 1902 ]] && [[ "$ARCH" = "i686" ]] && exit 1

##Function to get the nth day week of the month, for instance, Third Monday of March.

function nth_xday_of_month
{

my_nth=$1
my_xday=$2
my_month=$3
my_year=$4

case "$my_nth" in

1)  mydate=$(echo {01..07})
    ;;
2)  mydate=$(echo {08..14})
    ;;
3)  mydate=$(seq 15 21)
    ;;
4)  mydate=$(seq 22 28)
   ;;
5)  mydate=$(seq 29 31)
    ;;
*) echo "Echo wrong day of the week"
   exit 1
   ;;
esac


for x in $mydate; do
  nthday=$(date '+%u' -d "${my_year}${my_month}${x}")
  if [ "$nthday" -eq "$my_xday" ]; then
   date "${dformat}" -d "${my_year}${my_month}${x}"
  fi
done
}


##Memorial day - Last Monday of May.

for x in {31..01}; do y=$(date '+%u' -d "${myyear}05${x}"); if [ "$y" -eq 1 ]; then memday="${x}" ; break; fi ; done

echo "New Year's Day:              " $(date "${dformat}"  -d "${myyear}0101")
echo "Martin Luther King, Jr. Day: " $(nth_xday_of_month 3 1 01 ${myyear})
echo "Washington's Birthday:       " $(nth_xday_of_month 3 1 02 ${myyear})
echo "Memorial Day:                " $(date "${dformat}" -d "${myyear}05${memday}")
echo "Independence Day:            " $(date "${dformat}" -d "${myyear}0704")
echo "Labor Day:                   " $(nth_xday_of_month 1 1 09 ${myyear})
echo "Columbus Day:                " $(nth_xday_of_month 2 1 10 ${myyear})
echo "Veteran's Day:               " $(date "${dformat}" -d "${myyear}1111")
echo "Thanksgiving:                " $(nth_xday_of_month 4 4 11 ${myyear})
echo "Christmas Day:               " $(date "${dformat}" -d "${myyear}1225")

: <<'federal_holidays_comment'

http://en.wikipedia.org/wiki/Federal_holidays_in_the_United_States

Jan 1 - New Year's Day - 1st day of the year
Third Monday of January - Martin Luther King, Jr. Day 
Third Monday of February - Washington's Birthday
Last Monday of May - Memorial Day.
July 4 - Independence Day.
First Monday of September - Labor Day.
Second Monday of October - Columbus Day.
November 11 - Veteran's Day.
Fourth Thursday of November - Thanksgiving
December 25 - Christmas Day
federal_holidays_comment

One of the things which confuses many Linux users is why the access time attribute of a file does not change, although the file has been clearly accessed a number of times recently. Let me illustrate here by accessing a file, and checking whether the access time changes or not. I will use

 stat -c %x filename 

to grab the atime attribue.

[root@ip-10-136-87-176 lvm]# sleep 10; date; cat myfile ; stat -c %x myfile
Sun Aug  3 20:56:51 UTC 2014
Beam me up, Scotty.
2014-08-03 20:54:40.000000000 +0000
[root@ip-10-136-87-176 lvm]# sleep 10; date; cat myfile ; stat -c %x myfile
Sun Aug  3 20:57:23 UTC 2014
Beam me up, Scotty.
2014-08-03 20:54:40.000000000 +0000

The atime has not changed. Let us check

/proc/mounts

for any mount options.

[root@ip-10-136-87-176 lvm]# pwd
/mnt/lvm
[root@ip-10-136-87-176 lvm]# grep /mnt/lvm /proc/mounts 
/dev/xvdj1 /mnt/lvm ext3 rw,seclabel,relatime,errors=continue,barrier=1,data=ordered 0 0

The answer to our question lies in the

relatime

option.The Linux Kernel starting from version 2.6.30 switched to using the relatime by default during file system mount. Here is the exerpts from the man page for mount command –

relatime
              Update inode access times relative to modify or change time.  Access time is only updated  if  the
              previous  access time was earlier than the current modify or change time. (Similar to noatime, but
              doesn’t break mutt or other applications that need to know if a file has been read since the  last
              time it was modified.)

              Since  Linux  2.6.30,  the kernel defaults to the behavior provided by this option (unless noatime
              was  specified), and the strictatime option is required to obtain traditional semantics. In  addi-
              tion,  since  Linux 2.6.30, the file’s last access time is always  updated  if  it  is more than 1
              day old.

If the Kernel was to update the atime everytime a file was accessed that would be a big performance killer for disks. Specially in servers with lots of files which are accessed frequently, updating the atime attribute everytime a file is accessed would be a huge I/O burden, that is why the Kernel defaults to relatime. But as always, the Linux Kernel provides you the mechanism to update the atime everytime a file is accessed if you want to. For this to work you can use the

strictatime

option during mount. Let me illustrate this –

[root@ip-10-136-87-176 /]# umount /mnt/lvm
[root@ip-10-136-87-176 /]# mount  -o strictatime /dev/xvdj1 /mnt/lvm/
[root@ip-10-136-87-176 /]# grep '/mnt/lvm' /proc/mounts 
/dev/xvdj1 /mnt/lvm ext3 rw,seclabel,errors=continue,barrier=1,data=ordered 0 0
[root@ip-10-136-87-176 /]# cd /mnt/lvm/
[root@ip-10-136-87-176 lvm]# sleep 10; date; cat myfile ; stat -c %x myfile
Sun Aug  3 21:06:22 UTC 2014
Beam me up, Scotty.
2014-08-03 21:06:22.000000000 +0000
[root@ip-10-136-87-176 lvm]# sleep 60; date; cat myfile ; stat -c %x myfile
Sun Aug  3 21:07:27 UTC 2014
Beam me up, Scotty.
2014-08-03 21:07:27.000000000 +0000

Note: If the file system is mounted with a readonly option, the atime won’t be updated for obvious reasons.