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" Sun

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

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

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.

—

Alan

**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.

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 else year=$(( $year - 1 )) fi done

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!

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.

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

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

Copyright © 1994 - 2018 Linux Journal. All rights reserved.