LJ Archive


Fun with Days and Dates

I always enjoy learning tricks from Dave Taylor's Work the Shell column, and I understand that sometimes the example problems need to be somewhat contrived to fit within an article.

However, I think he might be thinking too hard about how to determine the day of the week for a date in the past. I use this simple one-liner:

$ date -d "7/20/1969" +"%a"

You also can use %w for a numeric 0–6 answer:

$ date -d "7/20/1969" +"%w"

I also use a variant of this command to convert from “seconds since epoch”, which often is found in log files, to a more readable date format:

$ date -d @1234567890
Fri Feb 13 18:31:30 EST 2009

As always, thanks for the tips, Dave. I hope you enjoy these in return.


Dave Taylor replies: Thanks for the message. The problem is that not all versions of Linux have this more sophisticated date command. I'm old-school. I have no problem with long, complicated solutions anyway.

Calendar Calculation

I have been reading Dave Taylor's command-line column (Work the Shell) in Linux Journal for some time, and I think that, although his command-line solutions are, in many ways, quite useful, it looks like he seems to have some problems with creating algorithms to solve the core portion of the problems.

For example, I have no problem with his parsing of the input, or loop controls, in the calendar program, but I think I have come up with a much more effective solution to the portion of the problem related to the determination of the year, by looking at the problem in a different way.

I examined the problem and decided that you don't actually need to search for the day in the calendar itself, in order to determine where it is in its week. All you need to know is what day of the week it will fall on, if you look at the calendar of the given month.

To do this, you can examine a generic month and, from that, determine where in the week the month begins. So, I came up with the following solution.

Given: 1) month in which event occurred (month), 2) day of month on which event occurred (dom), 3) day of week on which $dom occurred (dow). Find: year in which $dom occurred on $dow, in $month (year).

1. Label the days of the week as follows: Sun = 6, Mon = 5, ..., Fri = 1, Sat = 0.

2. Assign the value for dow, using the appropriate value, from above.

3. Calculate how many days will be in the first week of the target month:

days=$(( $(( $dom + $dow - 1 )) % 7 + 1 ))

Now, you know how many days are in the first week of this calendar month, in the target calendar year ($days).

So, you can find out if the test month matches this value, like this (well, not really, but if you compare the two values, this will tell if you've found the right year). Also, this awk script is predicated on the fact that cal has its first line of days on the third line of its output (FNR == 3):

cal $month $year | awk "FNR == 3 { print NF }"

If this value equals $days, then the current value of $year is the correct year; otherwise, you will have to loop, decrementing $year (by one) at each iteration, until the value for $year is correct.

My version of the loop looks like this. Please see if you can make it better! Specifically, I ended up having to create another variable, $caldays (calendar days, the number of days in the first week of the test month). Notice that in order to make even this work, I had to enclose the entire thing in backticks, or I got errors:

while true ; do
  caldays=`cal $month $year | awk "FNR == 3 { print NF }"`
  if [ $caldays -eq $days ] ; then
    cal $month $year
    exit 0           #    non-error exit
    year=$(( $year - 1 ))

By the way, the most years you will need to go back is ten (except, of course, when $month=February and $dom=29, in which case, you may have to go back significantly farther, as this condition can occur only in a year divisible by four ($year %4 -eq 0))! Also, this version of the program actually prints the calendar for the target month (cal $month $year). I just realized that this script does not check to make certain that the month actually contains $dom, but that realistically should be checked before searching for the existence of the target date, else the input data is/are invalid—that is, September, April, June and November have only 30 days; Feb has 28 (or 29, in leap years) days, and the rest have 31 days, but I know you already knew that.

Dave Johnson

Dave Taylor replies: Thanks for your note and interesting solution. As with any script, there are a number of different solution paths. What I'm really trying to illustrate in my Work the Shell column is the “solution journey” more than how to find and implement the optimal algorithm. I knew from the get-go that there were better mathematical formulas I could use, and indeed, one colleague has assured me that there's a mathematical formula that will solve this puzzle without any invocations of cal or anything like that. I'm unable to find it with Google, but that's another story. In any case, thanks for your smart and interesting solution!

Correction to Letters

In the Letters section in the September 2011 issue, the Letter titled “What Day Is It?”, and the script provided therein, was written by Peter Ljubic (not Eric Miller). We apologize for the error.—Ed.

Correction to “Linux Standard Base: State of Affairs”

In our article, “Linux Standard Base: State of Affairs”, in the August 2011 issue, one of our timeline graphics reported the addition of Java to LSB 4.0, without mentioning that it was added as a “trial-use” standard (proposed for inclusion, but not required). We regret the error.

Jeff Licquia

Linux in the Wild

This was me, not too long ago, over Aptos, California (Monterey Bay), near my home. It was my fourth tandem jump, but when I went back and looked at the pics, I thought “this should be in LJ!” It's my favorite LJ shirt, “May the source be with you”.

Rob Polk

LJ Archive