home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / perl / scripts-osu / date.shar < prev    next >
Encoding:
Text File  |  1991-02-10  |  9.3 KB  |  353 lines

  1. Path: tut.cis.ohio-state.edu!zaphod.mps.ohio-state.edu!uakari.primate.wisc.edu!dali.cs.montana.edu!milton!uw-beaver!ubc-cs!news-server.csri.toronto.edu!utgpu!cunews!cognos!garyp
  2. From: garyp@cognos.UUCP (Gary Puckering)
  3. Newsgroups: comp.lang.perl
  4. Subject: Date subroutines and calculator
  5. Message-ID: <9295@cognos.UUCP>
  6. Date: 7 Feb 91 04:58:10 GMT
  7. Reply-To: garyp@cognos.UUCP (Gary Puckering)
  8. Organization: Cognos Inc., Ottawa, Canada
  9. Lines: 341
  10.  
  11. In a recent posting someone was looking for perl routines that
  12. manipulate dates.  Here's a perl library that implements the standard
  13. jday and jdate functions (as described in Collected Algorithms of
  14. the ACM).  There are also routines which return the month name and
  15. weekday name given a month number of weekday number.  And there are routines
  16. that return the Julian day number for today, tomorrow and yesterday.
  17.  
  18. As a bonus prize, you also get an RPN-style date calculator.  Similar to bc,
  19. it also allows you to push perl expressions onto the stack -- thanks to
  20. the magic of `eval'.  Moreover, your expression can contain dates (like
  21. Jan 1, 1991) or functions like `today', `tomorrow' or `yesterday'.
  22.  
  23. Just cut everything below the cut line and feed it to sh.  You'll get
  24. date.pl (the subroutine library) and dtc (the calculator).  You'll
  25. probably want to edit the first line of dtc.
  26.  
  27. I would have like to have included routines that scan for any date format
  28. and extract it, but I haven't gotten around to it yet.  Consequently, dtc
  29. supports only a few date formats.  Sorry, but what's here is useful enough.
  30.  
  31. Disclaimer:  no warranty is expressed or implied
  32.  
  33. Right to copy:  you can do anything you want with this (but if you make
  34.                 lots of money from it, send me some)
  35.  
  36. ------------------------ cut line ------------------------------------
  37. #!/bin/sh
  38. # This is a shell archive, meaning:
  39. # 1. Remove everything above the #!/bin/sh line.
  40. # 2. Save the resulting text in a file.
  41. # 3. Execute the file with /bin/sh (not csh) to create the files:
  42. #    date.pl
  43. #    dtc
  44. # This archive created: Wed Feb  6 23:45:04 1991
  45. # By:    Gary Puckering ()
  46. export PATH; PATH=/bin:$PATH
  47. if test -f 'date.pl'
  48. then
  49.     echo shar: over-writing existing file "'date.pl'"
  50. fi
  51. cat << \SHAR_EOF > 'date.pl'
  52. package date;
  53.  
  54. # The following defines the first day that the Gregorian calendar was used
  55. # in the British Empire (Sep 14, 1752).  The previous day was Sep 2, 1752
  56. # by the Julian Calendar.  The year began at March 25th before this date.
  57.  
  58. $brit_jd = 2361222;
  59.  
  60. sub main'jdate
  61. # Usage:  ($month,$day,$year,$weekday) = &jdate($julian_day)
  62. {
  63.     local($jd) = @_;
  64.     local($jdate_tmp);
  65.     local($m,$d,$y,$wkday);
  66.  
  67.     warn("warning:  pre-dates British use of Gregorian calendar\n")
  68.         if ($jd < $brit_jd);
  69.  
  70.     $wkday = ($jd + 1) % 7;       # calculate weekday (0=Sun,6=Sat)
  71.     $jdate_tmp = $jd - 1721119;
  72.     $y = int((4 * $jdate_tmp - 1)/146097);
  73.     $jdate_tmp = 4 * $jdate_tmp - 1 - 146097 * $y;
  74.     $d = int($jdate_tmp/4);
  75.     $jdate_tmp = int((4 * $d + 3)/1461);
  76.     $d = 4 * $d + 3 - 1461 * $jdate_tmp;
  77.     $d = int(($d + 4)/4);
  78.     $m = int((5 * $d - 3)/153);
  79.     $d = 5 * $d - 3 - 153 * $m;
  80.     $d = int(($d + 5) / 5);
  81.     $y = 100 * $y + $jdate_tmp;
  82.     if($m < 10) {
  83.         $m += 3;
  84.     } else {
  85.         $m -= 9;
  86.         ++$y;
  87.     }
  88.     ($m, $d, $y, $wkday);
  89. }
  90.  
  91.  
  92. sub main'jday
  93. # Usage:  $julian_day = &jday($month,$day,$year)
  94. {
  95.     local($m,$d,$y) = @_;
  96.     local($ya,$c);
  97.  
  98.     $y = (localtime(time))[5] + 1900  if ($y eq '');
  99.  
  100.     if ($m > 2) {
  101.         $m -= 3;
  102.     } else {
  103.         $m += 9;
  104.         --$y;
  105.     }
  106.     $c = int($y/100);
  107.     $ya = $y - (100 * $c);
  108.     $jd =  int((146097 * $c) / 4) +
  109.            int((1461 * $ya) / 4) +
  110.            int((153 * $m + 2) / 5) +
  111.            $d + 1721119;
  112.     warn("warning:  pre-dates British use of Gregorian calendar\n")
  113.         if ($jd < $brit_jd);
  114.     $jd;
  115. }
  116.  
  117. sub main'is_jday
  118. {
  119. # Usage:  if (&is_jday($number)) { print "yep - looks like a jday"; }
  120.     local($is_jday) = 0;
  121.     $is_jday = 1 if ($_[0] > 1721119);
  122. }
  123.  
  124. sub main'monthname
  125. # Usage:  $month_name = &monthname($month_no)
  126. {
  127.     local($n,$m) = @_;
  128.     local(@names) = ('January','February','March','April','May','June',
  129.                      'July','August','September','October','November',
  130.                      'December');
  131.     if ($m ne '') {
  132.         substr($names[$n-1],0,$m);
  133.     } else {
  134.         $names[$n-1];
  135.     }
  136. }
  137.  
  138. sub main'monthnum
  139. # Usage:  $month_number = &monthnum($month_name)
  140. {
  141.     local($name) = @_;
  142.     local(%names) = (
  143.         'JAN',1,'FEB',2,'MAR',3,'APR',4,'MAY',5,'JUN',6,'JUL',7,'AUG',8,
  144.         'SEP',9,'OCT',10,'NOV',11,'DEC',12);
  145.     $name =~ tr/a-z/A-Z/;
  146.     $name = substr($name,0,3);
  147.     $names{$name};
  148. }
  149.  
  150. sub main'weekday
  151. # Usage:  $weekday_name = &weekday($weekday_number)
  152. {
  153.     local($wd) = @_;
  154.     ("Sun","Mon","Tue","Wed","Thu","Fri","Sat")[$wd];
  155. }
  156.  
  157. sub main'today
  158. # Usage:  $today_julian_day = &today()
  159. {
  160.     local(@today) = localtime(time);
  161.     local($d) = $today[3];
  162.     local($m) = $today[4];
  163.     local($y) = $today[5];
  164.     $m += 1;
  165.     $y += 1900;
  166.     &main'jday($m,$d,$y);
  167. }
  168.     
  169. sub main'yesterday
  170. # Usage:  $yesterday_julian_day = &yesterday()
  171. {
  172.     &main'today() - 1;
  173. }
  174.     
  175. sub main'tomorrow
  176. # Usage:  $tomorrow_julian_day = &tomorrow()
  177. {
  178.     &main'today() + 1;
  179. }
  180.     
  181. SHAR_EOF
  182. if test -f 'dtc'
  183. then
  184.     echo shar: over-writing existing file "'dtc'"
  185. fi
  186. cat << \SHAR_EOF > 'dtc'
  187. #!/usr/local/bin/perl -I/home/garyp/perl
  188.  
  189. require 'date.pl';
  190.  
  191. $command = '';
  192. print "    Date Calculator version 1.0\n";
  193. print "       (type `h' for help)\n";
  194. print "> ";
  195.  
  196. while(<stdin>) {
  197.     ($command) = /^\s*(\w+)\s*$/;
  198.     last if (index("quit",$command) == 0);
  199.     if (/^\s*(\d+)\s+(\d+)\s+(\d+)\s*$/) {            # quit
  200.         $j = &jday($1,$2,$3);
  201.         push(@stack,$j);
  202.         next;
  203.     }
  204.     elsif (/^\s*(\w+)\s+(\d+)(\s+(\d+)?)\s*$/) {    # mmm dd yy
  205.         # assumes this year if year is missing
  206.         $j = &jday(&monthnum($1),$2,$4);
  207.         push(@stack,$j);
  208.         next;
  209.     }
  210.     elsif (/^\s*([-]?\d+)\s*$/) {                    # [-]n
  211.         push(@stack,$1);
  212.         next;
  213.     }
  214.     elsif (index("clear",$command)==0) {            # clear
  215.         @stack = ();
  216.         next;
  217.     }
  218.     elsif (index("duplicate",$command)==0) {        # duplicate
  219.         push(@stack,$stack[$#stack]);
  220.         next;
  221.     }
  222.     elsif (index("exchange",$command)==0 ||
  223.            $command eq 'x') {                        # exchange
  224.         $x = pop(@stack);
  225.         $y = pop(@stack);
  226.         push(@stack,$x);
  227.         push(@stack,$y);
  228.         next;
  229.     }
  230.     elsif (index("print",$command)==0) {            # print
  231.         do print($stack[$#stack]);
  232.         next;
  233.     }
  234.     elsif (index("today",$command)==0) {            # today
  235.         push(@stack,&today());
  236.         do print($stack[$#stack]);
  237.         next;
  238.     }
  239.     elsif (/^\s*[+]\s*$/) {                            # add
  240.         $y = pop(@stack);
  241.         $x = pop(@stack);
  242.         if (&is_jday($x) && &is_jday($y)) {
  243.             print stderr "** cannot add two dates\n";
  244.             push(@stack,$x);
  245.             push(@stack,$y);
  246.             next;
  247.         }
  248.         $r = $x + $y;
  249.         push(@stack,$r);
  250.         do print($r);
  251.         next;
  252.     }
  253.     elsif (m:^\s*([\-*/%])\s*$:) {                    # (-) (*) (/) and (%)
  254.         $y = pop(@stack);
  255.         $x = pop(@stack);
  256.         $r = eval "$x $+ $y";
  257.         warn "** evaluation error $@\n" if $@ ne "";
  258.         push(@stack,$r);
  259.         do print($r);
  260.         next;
  261.     }
  262.     elsif (index("Print",$command)==0) {                # dump
  263.         do dump();
  264.         next;
  265.     }
  266.     elsif (index("help",$command)==0) {                    # help
  267.         print <<EOD ;
  268. Commands:
  269.  
  270.     mmm dd        Push date for current year onto stack
  271.     mmm dd yyyy    Push date onto stack
  272.     n or -n        Push positive/negative constant or interval onto stack
  273.     + - * / %    Add, subtract, multiply, divide, modulo
  274.     expr        Push result of Perl expression onto stack
  275.     <d>uplicate    Push a duplicate of the top value onto the stack
  276.     <c>lear        Clear stack
  277.     <p>rint        Print last value on stack
  278.     <P>rint        Print all stack values
  279.     <t>oday        Put today's date on the stack
  280.     e<x>change    Exchange top two values of stack
  281.     <q>uit        Exit the program
  282.  
  283. Note:   expressions are scanned for embedded dates of the form `1991/Jan/2',
  284.         `Jan 1, 1991' or just `Jan 1'.  These dates are translated to Julian
  285.         Day numbers before the expression is evaluated.  Also, the tokens
  286.         `today', `tomorrow' and `yesterday' are replaced with their
  287.         respective Julian Day numbers.  If the expression does something
  288.         stupid with Julian Day numbers (like add them) you get silly
  289.         results.
  290. EOD
  291.         next;
  292.     }
  293.     else {
  294.         chop;
  295.         # replace yyyy/mmm/dd dates with Julian day number
  296.           s|(\d{1,4})\W?(\w\w\w)\W?(\d\d?)|&jday(&monthnum($2),$3,$1)|ge;
  297.         # replace mmm dd yyyy dates with Julian day number
  298.           s|(\w\w\w)[\W\s](\d\d?)[,]?[\W\s](\d{1,4})|&jday(&monthnum($1),$2,$3)|ge;
  299.         # replace mmm dd dates with Julian day number (for this year)
  300.           s|(\w\w\w)[\W\s](\d\d?)|&jday(&monthnum($1),$2)|ge;
  301.         # replace 'today' with todays jday
  302.           s|\b(today)\b|&today()|ge;
  303.         # replace 'tomorrow' with tomorrows jday
  304.           s|\b(tomorrow)\b|&tomorrow()|ge;
  305.         # replace 'yesterday' with yesterdays jday
  306.           s|\b(yesterday)\b|&yesterday()|ge;
  307.         print $_,"\n";
  308.         push(@stack,eval($_));
  309.         do print($stack[$#stack]);
  310.         next;
  311.     }
  312. #    else { warn "** invalid command - try \"help\"\n" unless ($_ eq "\n"); }
  313. } continue {
  314.     print "> ";
  315.     $command = "";
  316. }
  317.  
  318. sub print #(value)
  319. {
  320.     if (&is_jday($_[0])) {
  321.         ($m,$d,$y,$wd) = &jdate($_[0]);
  322.         $month = &monthname($m,3);
  323.         $wkday = &weekday($wd);
  324.         print "= $wkday $month $d, $y (JD = $_[0])\n";
  325.     } else {
  326.         if ($_[0] > 365 || $_[0] < -365) {
  327.             $years = int($_[0] / 365.25);
  328.             $days = $_[0] - int($years * 365.25);
  329.             print "= $_[0] days  ($years years, $days days)\n\n";
  330.         } else {
  331.             print "= $_[0] days\n\n";
  332.         }
  333.     }
  334. }
  335.  
  336. sub dump
  337. {
  338.     for ($i = 0; $i <= $#stack; $i++) {
  339.         print "stack[",$i,"] ";
  340.         do print($stack[$i]);
  341.     }
  342. }
  343. SHAR_EOF
  344. chmod +x 'dtc'
  345. #    End of shell archive
  346. exit 0
  347. -- 
  348. Gary Puckering                             Cognos Incorporated
  349.   VOICE: (613) 738-1338 x6100              P.O. Box 9707
  350.   UUCP:  uunet!mitel!cunews!cognos!garyp   Ottawa, Ontario
  351.   INET:  garyp%cognos.uucp@uunet.uu.net    CANADA  K1G 3Z4
  352.  
  353.