#/bin/sh #In a recent posting someone was looking for perl routines that #manipulate dates. Here's a perl library that implements the standard #jday and jdate functions (as described in Collected Algorithms of #the ACM). There are also routines which return the month name and #weekday name given a month number of weekday number. And there are routines #that return the Julian day number for today, tomorrow and yesterday. # #As a bonus prize, you also get an RPN-style date calculator. Similar to bc, #it also allows you to push perl expressions onto the stack -- thanks to #the magic of `eval'. Moreover, your expression can contain dates (like #Jan 1, 1991) or functions like `today', `tomorrow' or `yesterday'. # #Just cut everything below the cut line and feed it to sh. You'll get #date.pl (the subroutine library) and dtc (the calculator). You'll #probably want to edit the first line of dtc. # #I would have like to have included routines that scan for any date format #and extract it, but I haven't gotten around to it yet. Consequently, dtc #supports only a few date formats. Sorry, but what's here is useful enough. # #Disclaimer: no warranty is expressed or implied # #Right to copy: you can do anything you want with this (but if you make # lots of money from it, send me some) # #------------------------ cut line ------------------------------------ #/bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #!/bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # date.pl # dtc # This archive created: Wed Feb 6 23:45:04 1991 # By: Gary Puckering () export PATH; PATH=/bin:$PATH if test -f 'date.pl' then echo shar: over-writing existing file "'date.pl'" fi cat << \SHAR_EOF > 'date.pl' package date; # The following defines the first day that the Gregorian calendar was used # in the British Empire (Sep 14, 1752). The previous day was Sep 2, 1752 # by the Julian Calendar. The year began at March 25th before this date. $brit_jd = 2361222; sub main'jdate # Usage: ($month,$day,$year,$weekday) = &jdate($julian_day) { local($jd) = @_; local($jdate_tmp); local($m,$d,$y,$wkday); warn("warning: pre-dates British use of Gregorian calendar\n") if ($jd < $brit_jd); $wkday = ($jd + 1) % 7; # calculate weekday (0=Sun,6=Sat) $jdate_tmp = $jd - 1721119; $y = int((4 * $jdate_tmp - 1)/146097); $jdate_tmp = 4 * $jdate_tmp - 1 - 146097 * $y; $d = int($jdate_tmp/4); $jdate_tmp = int((4 * $d + 3)/1461); $d = 4 * $d + 3 - 1461 * $jdate_tmp; $d = int(($d + 4)/4); $m = int((5 * $d - 3)/153); $d = 5 * $d - 3 - 153 * $m; $d = int(($d + 5) / 5); $y = 100 * $y + $jdate_tmp; if($m < 10) { $m += 3; } else { $m -= 9; ++$y; } ($m, $d, $y, $wkday); } sub main'jday # Usage: $julian_day = &jday($month,$day,$year) { local($m,$d,$y) = @_; local($ya,$c); $y = (localtime(time))[5] + 1900 if ($y eq ''); if ($m > 2) { $m -= 3; } else { $m += 9; --$y; } $c = int($y/100); $ya = $y - (100 * $c); $jd = int((146097 * $c) / 4) + int((1461 * $ya) / 4) + int((153 * $m + 2) / 5) + $d + 1721119; warn("warning: pre-dates British use of Gregorian calendar\n") if ($jd < $brit_jd); $jd; } sub main'is_jday { # Usage: if (&is_jday($number)) { print "yep - looks like a jday"; } local($is_jday) = 0; $is_jday = 1 if ($_[0] > 1721119); } sub main'monthname # Usage: $month_name = &monthname($month_no) { local($n,$m) = @_; local(@names) = ('January','February','March','April','May','June', 'July','August','September','October','November', 'December'); if ($m ne '') { substr($names[$n-1],0,$m); } else { $names[$n-1]; } } sub main'monthnum # Usage: $month_number = &monthnum($month_name) { local($name) = @_; local(%names) = ( 'JAN',1,'FEB',2,'MAR',3,'APR',4,'MAY',5,'JUN',6,'JUL',7,'AUG',8, 'SEP',9,'OCT',10,'NOV',11,'DEC',12); $name =~ tr/a-z/A-Z/; $name = substr($name,0,3); $names{$name}; } sub main'weekday # Usage: $weekday_name = &weekday($weekday_number) { local($wd) = @_; ("Sun","Mon","Tue","Wed","Thu","Fri","Sat")[$wd]; } sub main'today # Usage: $today_julian_day = &today() { local(@today) = localtime(time); local($d) = $today[3]; local($m) = $today[4]; local($y) = $today[5]; $m += 1; $y += 1900; &main'jday($m,$d,$y); } sub main'yesterday # Usage: $yesterday_julian_day = &yesterday() { &main'today() - 1; } sub main'tomorrow # Usage: $tomorrow_julian_day = &tomorrow() { &main'today() + 1; } SHAR_EOF if test -f 'dtc' then echo shar: over-writing existing file "'dtc'" fi cat << \SHAR_EOF > 'dtc' #/usr/local/bin/perl -I/home/garyp/perl require 'date.pl'; $command = ''; print " Date Calculator version 1.0\n"; print " (type `h' for help)\n"; print "> "; while() { ($command) = /^\s*(\w+)\s*$/; last if (index("quit",$command) == 0); if (/^\s*(\d+)\s+(\d+)\s+(\d+)\s*$/) { # quit $j = &jday($1,$2,$3); push(@stack,$j); next; } elsif (/^\s*(\w+)\s+(\d+)(\s+(\d+)?)\s*$/) { # mmm dd yy # assumes this year if year is missing $j = &jday(&monthnum($1),$2,$4); push(@stack,$j); next; } elsif (/^\s*([-]?\d+)\s*$/) { # [-]n push(@stack,$1); next; } elsif (index("clear",$command)==0) { # clear @stack = (); next; } elsif (index("duplicate",$command)==0) { # duplicate push(@stack,$stack[$#stack]); next; } elsif (index("exchange",$command)==0 || $command eq 'x') { # exchange $x = pop(@stack); $y = pop(@stack); push(@stack,$x); push(@stack,$y); next; } elsif (index("print",$command)==0) { # print do print($stack[$#stack]); next; } elsif (index("today",$command)==0) { # today push(@stack,&today()); do print($stack[$#stack]); next; } elsif (/^\s*[+]\s*$/) { # add $y = pop(@stack); $x = pop(@stack); if (&is_jday($x) && &is_jday($y)) { print stderr "** cannot add two dates\n"; push(@stack,$x); push(@stack,$y); next; } $r = $x + $y; push(@stack,$r); do print($r); next; } elsif (m:^\s*([\-*/%])\s*$:) { # (-) (*) (/) and (%) $y = pop(@stack); $x = pop(@stack); $r = eval "$x $+ $y"; warn "** evaluation error $@\n" if $@ ne ""; push(@stack,$r); do print($r); next; } elsif (index("Print",$command)==0) { # dump do dump(); next; } elsif (index("help",$command)==0) { # help print <uplicate Push a duplicate of the top value onto the stack lear Clear stack

rint Print last value on stack

rint Print all stack values oday Put today's date on the stack echange Exchange top two values of stack uit Exit the program Note: expressions are scanned for embedded dates of the form `1991/Jan/2', `Jan 1, 1991' or just `Jan 1'. These dates are translated to Julian Day numbers before the expression is evaluated. Also, the tokens `today', `tomorrow' and `yesterday' are replaced with their respective Julian Day numbers. If the expression does something stupid with Julian Day numbers (like add them) you get silly results. EOD next; } else { chop; # replace yyyy/mmm/dd dates with Julian day number s|(\d{1,4})\W?(\w\w\w)\W?(\d\d?)|&jday(&monthnum($2),$3,$1)|ge; # replace mmm dd yyyy dates with Julian day number s|(\w\w\w)[\W\s](\d\d?)[,]?[\W\s](\d{1,4})|&jday(&monthnum($1),$2,$3)|ge; # replace mmm dd dates with Julian day number (for this year) s|(\w\w\w)[\W\s](\d\d?)|&jday(&monthnum($1),$2)|ge; # replace 'today' with todays jday s|\b(today)\b|&today()|ge; # replace 'tomorrow' with tomorrows jday s|\b(tomorrow)\b|&tomorrow()|ge; # replace 'yesterday' with yesterdays jday s|\b(yesterday)\b|&yesterday()|ge; print $_,"\n"; push(@stack,eval($_)); do print($stack[$#stack]); next; } # else { warn "** invalid command - try \"help\"\n" unless ($_ eq "\n"); } } continue { print "> "; $command = ""; } sub print #(value) { if (&is_jday($_[0])) { ($m,$d,$y,$wd) = &jdate($_[0]); $month = &monthname($m,3); $wkday = &weekday($wd); print "= $wkday $month $d, $y (JD = $_[0])\n"; } else { if ($_[0] > 365 || $_[0] < -365) { $years = int($_[0] / 365.25); $days = $_[0] - int($years * 365.25); print "= $_[0] days ($years years, $days days)\n\n"; } else { print "= $_[0] days\n\n"; } } } sub dump { for ($i = 0; $i <= $#stack; $i++) { print "stack[",$i,"] "; do print($stack[$i]); } } SHAR_EOF chmod +x 'dtc' # End of shell archive exit 0 -- Gary Puckering Cognos Incorporated VOICE: (613) 738-1338 x6100 P.O. Box 9707 UUCP: uunet!mitel!cunews!cognos!garyp Ottawa, Ontario INET: garyp%cognos.uucp@uunet.uu.net CANADA K1G 3Z4