[net.sources] New Date Program...

taylor@hplabsc.UUCP (Dave Taylor) (01/06/87)

So there I was after having used the date(1) command on HP-UX (System V
with some neat enhancements, etc etc) and I got onto a Vax running BSD
Unix.  Well!  The date(1) command on BSD doesn't have all the neat
functionality that the HP-UX one has, so I rewrote the BSD date command!
What follows is a *nonshar* (too lazy) that contains the "C" source to
the newdate program and the man entry too.

The enhancement is that the user can now specify exactly what the
output format is to look like.  You can now have 'date' output a
format like "Today is Friday, December 12, 1986 at 4:50 pm" rather
than the old, boring stuff.  Neat, eh?

This has been enhanced too - I've added (among others) %z to output the
current timezone, so even you system V users should be interested!

Bugs, etc, to comp.sources.d please.

						-- Dave Taylor
						taylor@hplabs.HP.COM

-- attachment: shar files..

# Shell Archive created by hpldat!taylor at Mon Jan  5 21:33:12 1987

# To unpack the enclosed files, please use this file as input to the
# Bourne (sh) shell.  This can be most easily done by the command;
#     sh < thisfilename

# This archive contains;
#  newdate.1        newdate.c

# ---------- file newdate.1 ----------

filename="newdate.1"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file newdate.1...
fi

cat << 'END-OF-FILE' > $filename
.TH NEWDATE 1 LOCAL
.UC 4
.SH NAME
newdate \- enhanced version of date(1)
.SH SYNOPSIS
.B newdate
.RB "[ yymmddhhmm [ " . "ss ] ]"
.br
.B newdate
.RB "+format_statement"
.SH DESCRIPTION
If no arguments are given, or the first argument doesn't start
with a `+' character, the standard 
.I date(1)
program is invoked.  
.sp
If the argument begins with a `+' sign, then it is interpreted
as follows:
.nf

	%n	carriage return
	%t	tab
	%%	percent sign
	
	%A	name of the day of the week
	%D	date in MM/DD/YY format
	%H	hour (0-23)
	%M	minute (0-59)
	%N	name of the month
	%S	second (0-59)
	%T	time as HH:MM:SS
	%a	abbreviated name of the day of the week
	%d	day of month (0-31)
	%h	abbreviated name of the month
	%j	Julian date (0-364)
	%m	month number (0-11)
	%r	time as HH:MM am/pm
	%w	day of week (0-6, 0=Sunday)
	%y	year - 1900  (0-99)
	%z	time zone name

.fi
any other characters encountered in the format instruction
(which must start with the `+' character) are copied as
is to the output.
.SH EXAMPLE
A nice date format can be obtained by using
.nf

  \fInewdate "+Today is %A, %N 19%d, %y at %r'"\fR

.fi
which results in output like
.nf

  \fIToday is Friday, December 12, 1986 at 11:02 pm\fR

.sp
Another interesting example is to use
.nf

  \fInewdate +Date: %a %h %d, %y %T %z\fR

.fi
to get the standard mail header line:
.nf

  \fIDate: Fri Dec 12, 86 23:05:40 PST\fR

.fi
.SH FILES
/bin/date for calls to the ``real'' date program.
.SH SEE ALSO
date(1)
.SH AUTHOR
Dave Taylor, Hewlett-Packard Labs
.SH COMPATABILITY
This program implements the System V 
.I date(1)
command with three new (and useful) additions,
namely the '%A' (full day name) '%N' (full month name)
and '%z' (time zone name).
.SH COMMENTS
Since this program calls 
.I /bin/date
for all arguments other than those starting with a `+',
it is expected that this can be called ``date'' and be
placed somewhere in the users path so that this is the
default program for calls to 
.I date(1).
.sp
The notation is really disgusting, but I'm just implementing
what was created on System V.  I suppose the correct way to
phrase this is ``it's compatible!'', but still....
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 2140 ]
  then
    echo $filename changed - should be 2140 bytes, not $size bytes
  fi

  chmod 666 $filename
fi

# ---------- file newdate.c ----------

filename="newdate.c"

if [ -f $filename ]
then
  echo File \"$filename\" already exists\!  Skipping...
  filename=/dev/null		# throw it away
else
  echo extracting file newdate.c...
fi

cat << 'END-OF-FILE' > $filename
/**				newdate.c				**/

/** This program is implemented based on the manual entry in HP-UX
    (Hewlett-Packards *reliable* version of Unix) for the 'date(1)'
    program.  The main improvement is that the user now has a set
    of format commands that they can use to get output in a different
    format than the normal (ugly) date(1) command.  Note to system V
    and HP-UX users - I've also added %A and %N for full day and
    month name, respectively.

    Please see the manual entry for more information.

    (C) Copyright 1986, Dave Taylor
**/

/** If you're on a System V machine, then compile with -DSYSV as a flag. **/

#include <stdio.h>
#include <sys/time.h>

char *short_dayname[]   = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
char *short_monthname[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
		            "Aug", "Sep", "Oct", "Nov", "Dec" };

char *long_dayname[]    = { "Sunday", "Monday", "Tuesday", "Wednesday", 
			    "Thursday", "Friday", "Saturday" };
char *long_monthname[]  = { "January", "February", "March", "April", "May",
			    "June", "July", "August", "September", "October",
			    "November", "December" };

struct tm *localtime();		/* forward declare for compiler happiness */

#ifdef SYSV
  extern char *tzname[2];
#else
  char *timezone();		/* another forward declaration...  	*/
#endif

main(argc, argv)
int argc;
char *argv[];
{
	char buffer[200], 	/* our output buffer... 		*/
	     tempbuf[100];	/* and a temp one for formatting stuff  */
	int  loc;		/* our location in the format string    */
	long thetime;		/* the current time, in seconds!        */
	struct tm *t;		/* the time record structure		*/
#ifndef SYSV
	struct timeval	tp;	/* for storing yet-another-format       */
	struct timezone tz;	/* to figure out our timezone...        */
#endif

	if (argc == 1 || argv[1][0] != '+')   /* go to 'real' date prog */
	  execv("/bin/date", argv);

	if (argc > 2) {
	  fprintf(stderr,"Usage: %s [new time] [ + format string]\n",
		  argv[0]);
	  exit(1);
	}

	/* if we're here we're doing okay... */

	thetime = time( (long *) 0);
	t = localtime(&thetime);

#ifndef SYSV
	/** now let's get the timezone that we're in... **/

	gettimeofday(&tp, &tz);

#endif

	/* we have the time...now let's parse and build the output! */

	for (loc = 1; loc < strlen(argv[1]);) {
	  tempbuf[0] = '\0';

	  if (argv[1][loc] == '%') {	/* a format string! */

	    switch (argv[1][loc+1]) {

	      case 'n' : strcat(buffer, "\n");		break;
	      case 't' : strcat(buffer, "\t");		break;
	      case '%' : strcat(buffer, "%");		break;

	      case 'm' : sprintf(tempbuf, "%d", t->tm_mon);	break;
	      case 'd' : sprintf(tempbuf, "%d", t->tm_mday);	break;
	      case 'y' : sprintf(tempbuf, "%d", t->tm_year);	break;
	      case 'D' : sprintf(tempbuf, "%d/%d/%d",
			 t->tm_mon, t->tm_mday, t->tm_year);	break;
	      case 'H' : sprintf(tempbuf, "%d", t->tm_hour);	break;
	      case 'M' : sprintf(tempbuf, "%d", t->tm_min);	break;
	      case 'S' : sprintf(tempbuf, "%d", t->tm_sec);	break;
	      case 'T' : sprintf(tempbuf, "%02d:%02d:%02d",
			 t->tm_hour, t->tm_min, t->tm_sec);	break;
	      case 'j' : sprintf(tempbuf, "%d", t->tm_yday);	break;
	      case 'w' : sprintf(tempbuf, "%d", t->tm_wday);	break;
	      case 'a' : sprintf(tempbuf, "%s", 
				 short_dayname[t->tm_wday]); 	break;
	      case 'h' : sprintf(tempbuf, "%s", 
				short_monthname[t->tm_mon]); 	break;
	      case 'A' : sprintf(tempbuf, "%s", 
				 long_dayname[t->tm_wday]); 	break;
	      case 'N' : sprintf(tempbuf, "%s", 
				long_monthname[t->tm_mon]); 	break;
	      case 'r' : sprintf(tempbuf, "%d:%02d %s",
			 t->tm_hour > 12? t->tm_hour - 12:t->tm_hour,
		         t->tm_min,
			 t->tm_hour > 12? "pm":"am");		break;
	      case 'z' : sprintf(tempbuf, "%s",
#ifdef SYSV
			 t->tm_isdst? tzname[1] : tzame[0]);	break;
#else
			 timezone(tz.tz_minuteswest, t->tm_isdst));  break;
#endif

	      case '\0': fprintf(stderr,
			"%s: unexpected end of format instructions!\n",argv[0]);
			 exit(1);

	      default  : fprintf(stderr,
		         "%s: don't understand %%%c as a format instruction!\n",
			 argv[0], argv[1][loc+1]);
			 exit(1);
	    }

	    loc += 2;	/* skip the percent and the char we just dealt with */

	    if (tempbuf[0] != '\0') 
	      strcat(buffer, tempbuf);

	  }
	  else { 	/* not a percent sign... */

	    tempbuf[0] = argv[1][loc++];
	    tempbuf[1] = '\0';
	    strcat(buffer, tempbuf);

	  }
	}
	
	/* and print the buffer out! */

	printf("%s\n", buffer);
	
	exit(0);			/* bye! */
}
END-OF-FILE

if [ "$filename" != "/dev/null" ]
then
  size=`wc -c < $filename`

  if [ $size != 4546 ]
  then
    echo $filename changed - should be 4546 bytes, not $size bytes
  fi

  chmod 666 $filename
fi

echo done

exit 0