home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume7 / nag / part02 / gdate.c next >
Encoding:
C/C++ Source or Header  |  1986-11-30  |  13.2 KB  |  650 lines

  1. /*
  2.  * routines to turn a date from various formats to other formats
  3.  *
  4.  *    Primary interesting routine is gdate() which eats a date
  5.  *    string of several common formats (see comment) and
  6.  *    fills in a standard UNIX tm structure.
  7.  *
  8.  *    Barry Shein, Boston University
  9.  *
  10.  *    if you compile it -DDEBUG (with DEBUG defined) it will
  11.  *    pull in a main() routine to run standalone for testing.
  12.  *
  13.  *    NOTE:
  14.  *
  15.  *    Barry's gdate was broken by a 1-off error; tm_mon is kept
  16.  *    in the range 0..11 instead of 1..12.  Also, his totime was
  17.  *    broken, so I've deleted it and use the tm_to_time() function
  18.  *    from mod.sources.
  19.  *
  20.  *
  21.  * Defines the functions:
  22.  *
  23.  * lcase() -- convert a char to lower case
  24.  * dstring() -- get digit string from sp into bp (buffer) returning new sp
  25.  * skipw() -- skip white space returning updated ptr
  26.  * prefix() -- return how many chars of s1 prefix s2
  27.  * find() -- look up str in list for non-ambiguous (prefix) match
  28.  * lookup() -- like find but demands exact match
  29.  * extract() -- extract a token
  30.  * fill() -- fill up an area with a value (eg. zeros)
  31.  *
  32.  * gdate() -- convert a date/time string to a tm structure.
  33.  * gtime() -- convert time string to a tm structure.
  34.  *
  35.  * days() -- how many days were in a year.
  36.  * jan1() -- return day of the week of jan 1 of given year
  37.  * dowk() -- insert day of week given year and day of year into tm struct.
  38.  * doyr() -- insert partial day of year given yr, mon and mday into tm struct.
  39.  *
  40.  * leap() -- Return 1 if `y' is a leap year, 0 otherwise.
  41.  * ndays() -- number of days between UNIX epoch and time in a tm struct.
  42.  * tm_to_time() -- Convert a tm struct to a time_t.
  43.  */
  44.  
  45. #include <stdio.h>
  46. #include <ctype.h>
  47. #include <sys/types.h>
  48.  
  49. #ifdef SYS5
  50.  
  51. #    define    time_t    long    /* SV is inconsistent, so go with lcd */
  52.  
  53. #    include <time.h>
  54. #    include <sys/times.h>
  55. #    include <string.h>
  56.  
  57. # else                /* BSD */
  58.  
  59. #    include <sys/time.h>
  60. #    include <sys/timeb.h>
  61. #    include <strings.h>
  62.  
  63. #endif
  64.  
  65. /*----------------------------------------------------------------
  66.  *
  67.  * Manifest constants
  68.  *
  69.  */
  70.  
  71. #define MAXTOK 20
  72. #define AMBIG  -1    /* ambiguous month */
  73. #define FALSE  -2    /* bad syntax       */
  74.  
  75. /*----------------------------------------------------------------
  76.  *
  77.  *    static and global Data
  78.  *
  79.  */
  80.  
  81. char *months[] = {
  82.   "january", "february", "march", "april", "may", "june", "july",
  83.   "august", "september", "october", "november", "december", 0
  84. } ;
  85.   
  86. char *dow[] = {
  87.     "sunday", "monday", "tuesday", "wednesday", "thursday",
  88.     "friday", "saturday", 0
  89. } ;
  90.     
  91.     /*
  92.      *    known time-zone name list
  93.      */
  94. char *tz[] =
  95. {
  96.   "adt", "ast", "edt", "est", "cdt", "cst", "mst", "mdt",
  97.   "pdt", "pst", "gmt",
  98.   0
  99. } ;
  100.  
  101. char mdays[] = {
  102.   31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  103. } ;
  104.  
  105. /*----------------------------------------------------------------
  106.  *
  107.  *    Utility functions
  108.  *
  109.  */
  110.  
  111. /*
  112.  * lcase() -- convert a char to lower case
  113.  */
  114. lcase(c) register char c ;
  115. {
  116.   return(isupper(c) ? tolower(c) : c) ;
  117. }
  118.  
  119. /*
  120.  * dstring() -- get digit string from sp into bp (buffer) returning new sp
  121.  */
  122. char *
  123. dstring(bp,sp)
  124.      register char *sp, *bp  ;
  125. {
  126.   register int i = 0 ;
  127.   while(isdigit(*sp))
  128.     if(i++ == MAXTOK) break ;
  129.     else *bp++ = *sp++ ;
  130.   *bp = '\0' ;
  131.   return(sp) ;
  132. }
  133.  
  134. /*
  135.  * skipw() -- skip white space returning updated ptr
  136.  */
  137. char *
  138. skipw(sp) register char *sp ;
  139. {
  140.   while(isspace(*sp) || *sp == '-') ++sp ;
  141.   return(sp) ;
  142. }
  143.  
  144. /*
  145.  * prefix() -- return how many chars of s1 prefix s2
  146.  */
  147. prefix(s1,s2) register char *s1, *s2 ;
  148. {
  149.   register int i = 0  ;
  150.   
  151.   for(; *s1 == *s2 ; s1++,s2++,i++)
  152.     if(*s2 == '\0') break ;
  153.   return(*s1 == '\0' ? i : 0) ;
  154. }
  155.  
  156. /*
  157.  * find() -- look up str in list for non-ambiguous (prefix) match
  158.  */
  159. find(sp,lp) register char *sp, **lp ;
  160. {
  161.   int i,j,ambig = 0 ;
  162.   register int k ;
  163.   int ret  = FALSE ;
  164.   
  165.   for(i = 0,k = 0 ; *lp ; lp++,k++)
  166.     if((j  = prefix(sp,*lp)) > i)
  167.       {
  168.     ambig = 0 ;
  169.     i = j ;
  170.     ret = k + 1 ;
  171.       }
  172.     else if(j && (i == j)) ++ambig ;
  173.   return(ambig ? AMBIG : ret) ;
  174. }
  175.  
  176. /*
  177.  * lookup() -- like find but demands exact match
  178.  */
  179. lookup(sp,lp) register char *sp, **lp ;
  180. {
  181.   int i = 0 ;
  182.   
  183.   for(i=0 ; *lp ; lp++,i++)
  184.     if(strcmp(sp,*lp) == 0) return(i+1) ;
  185.   return(0) ;
  186. }
  187.  
  188. /*
  189.  * extract() -- extract a token
  190.  */
  191. char *
  192. extract(bp,sp) register char *bp, *sp ;
  193. {
  194.   register int i = 0 ;
  195.   
  196.   sp = skipw(sp) ;
  197.   for(; isalnum(*sp); sp++)
  198.     if(i++ == MAXTOK) break ;
  199.     else *bp++ = lcase(*sp) ;
  200.   *bp = '\0' ;
  201.   return(sp) ;
  202. }
  203.  
  204. /*
  205.  * fill() -- fill up an area with a value (eg. zeros)
  206.  */
  207. fill(cp,c,n) register char *cp, c ; register int n ;
  208. {
  209.   while(n--) *cp++ = c ;
  210. }
  211.  
  212. /*----------------------------------------------------------------
  213.  *
  214.  *    gdate, gtime related.
  215.  *
  216.  */
  217.  
  218. char *gdate() ;
  219. char *gtime() ;
  220.  
  221. /*
  222.  *    gdate(date_str_ptr,time_buf_ptr)
  223.  *    (see CTIME(3) in UPM for second arg format)
  224.  *    takes many reasonable date strings and translates to
  225.  *    the time-buf structure (integers)
  226.  *
  227.  *    formats (eg.):
  228.  *        oct 19, 1983
  229.  *        OcT 19, 1983 12:43
  230.  *        oCt 19, 1983 12:43:33
  231.  *        oct 19 1983 ...
  232.  *        19 oct 83 ....
  233.  *        10/19/83 12:43:33
  234.  *        19-OCT-83 12:43:00 (DEC style)
  235.  *        Wed Oct 19 12:43 EST 83 (UNIX style)
  236.  *        oct. 19, 83 1:44 pm est
  237.  *        831019124333    (see date(1))
  238.  *    some variations on those themes also.
  239.  *
  240.  *    BUGS: probably a few (maybe some in the eye of the beholder)
  241.  *        does not set dst flag (unless 'EDT' is in string)
  242.  */
  243. char *
  244. gdate(sp,tp) register char *sp ; register struct tm *tp ;
  245. {
  246.   char buf[MAXTOK] ;
  247.   
  248.   fill(tp,'\0',sizeof *tp) ;
  249.   sp = skipw(sp) ;
  250.   if(isdigit(*sp))    /* either '7/12/83' or '12 jul 83' */
  251.     {
  252.       if(isdigit(sp[1]) && isdigit(sp[2])) /* UNIX yymmddhhmmss */
  253.     {
  254.       buf[2] = '\0' ;
  255.       (void)strncpy(buf,sp,2) ;
  256.       tp->tm_year = atoi(buf) ;
  257.       (void)strncpy(buf,sp += 2,2) ;
  258.       tp->tm_mon = atoi(buf) - 1 ;
  259.       sp += 2 ;
  260.       if(!isdigit(*sp)) goto badday ;
  261.       (void)strncpy(buf,sp,2) ;
  262.       tp->tm_mday = atoi(buf) ;
  263.       sp += 2 ;
  264.       if(!isdigit(*sp)) goto check ;
  265.       (void)strncpy(buf,sp,2) ;
  266.       
  267.       /* ??? formerly null effect "tp->tm_hour ;" */
  268.       
  269.       tp->tm_hour = atoi(buf) ;
  270.       sp += 2 ;
  271.       if(!isdigit(*sp)) goto check ;
  272.       (void)strncpy(buf,sp,2) ;
  273.       tp->tm_min = atoi(buf) ;
  274.       sp += 2 ;
  275.       if(!isdigit(*sp)) goto check ;
  276.       (void)strncpy(buf,sp,2) ;
  277.       tp->tm_min = atoi(buf) ;
  278.       goto check ;
  279.     }
  280.       sp = dstring(buf,sp) ;
  281.       sp = skipw(sp) ;
  282.       if(*sp == '/')    /* must be '7/12/83' */
  283.     {
  284.       if((tp->tm_mon = atoi(buf) - 1) < 0 || (tp->tm_mon > 11))
  285.         {
  286.           tp->tm_mon = FALSE ;
  287.           goto badmon ;
  288.         }
  289.       sp = skipw(++sp) ;
  290.       if(!isdigit(*sp)) goto badday ;
  291.       sp = dstring(buf,sp) ;
  292.       tp->tm_mday = atoi(buf) ;
  293.       
  294.       sp = skipw(sp) ;
  295.       if(*sp != '/') goto badyr ;
  296.       sp = skipw(++sp) ;
  297.       if(!isdigit(*sp)) goto badyr ;
  298.       sp = dstring(buf,sp) ;
  299.       tp->tm_year = atoi(buf) ;
  300.       
  301.       sp = gtime(sp,tp) ;
  302.     }
  303.       else
  304.     {
  305.       /*
  306.        * must be '12 jul 83'
  307.        */
  308.       tp->tm_mday = atoi(buf) ;
  309.       
  310.       sp = extract(buf,sp) ;
  311.       if((tp->tm_mon = find(buf,months)) < 0) goto badmon ;
  312.       
  313.       if(*sp == '.') ++sp ;
  314.       sp = skipw(sp) ;
  315.       
  316.       if(!isdigit(*sp)) goto badyr ;
  317.       sp = dstring(buf,sp) ;
  318.       tp->tm_year = atoi(buf) ;
  319.       
  320.       sp = gtime(sp,tp) ;
  321.     }
  322.     }
  323.   else
  324.     {
  325.       int flag = 0 ;    /* used to indicate looking for UNIX style */
  326.       
  327.       /*
  328.        * either 'jul 12 83' or (UNIX) Wed jul 12 18:33 EST 1983
  329.        */
  330.       sp = extract(buf,sp) ;
  331.       if(find(buf,dow) > 0)
  332.     {
  333.       sp = extract(buf,sp) ;
  334.       ++flag ;
  335.     }
  336.       
  337.       if((tp->tm_mon = find(buf,months)) < 0) goto badmon ;
  338.       
  339.       if(*sp == '.') ++sp ;
  340.       sp = skipw(sp) ;
  341.       
  342.       if(!isdigit(*sp)) goto badday ;
  343.       sp = dstring(buf,sp) ;
  344.       tp->tm_mday = atoi(buf) ;
  345.       
  346.       sp = skipw(sp) ;
  347.       if(*sp == ',') sp++ ;
  348.       sp = skipw(sp) ;
  349.       
  350.       if(flag) sp = skipw(gtime(sp,tp)) ;
  351.       
  352.       if(!isdigit(*sp)) goto badyr ;
  353.       sp = dstring(buf,sp) ;
  354.       tp->tm_year = atoi(buf) ;
  355.       
  356.       if(!flag) sp = gtime(sp,tp) ;
  357.     }
  358.  check:
  359.   /*
  360.    * check for ridiculous numbers
  361.    */
  362.   if(tp->tm_mday < 1) goto badday ;
  363.   if(tp->tm_mday > mdays[tp->tm_mon])
  364.     if(!((tp->tm_mon == 1) &&    /* check for Feb 29 */
  365.      (tp->tm_mday == 29) && (days(tp->tm_year) == 365) ))
  366.       goto badday ;
  367.   if(tp->tm_year >= 1900) tp->tm_year -= 1900 ;
  368.   if(tp->tm_hour > 23)
  369.     {
  370.       tp->tm_hour = FALSE ;
  371.       return(sp) ;
  372.     }
  373.   if(tp->tm_min > 59)
  374.     {
  375.       tp->tm_hour = FALSE ;
  376.       return(sp) ;
  377.     }
  378.   if(tp->tm_sec > 59)
  379.     {
  380.       tp->tm_sec = FALSE ;
  381.       return(sp) ;
  382.     }
  383.   /*
  384.    *    fill in day of year, day of week (these calls must be
  385.    *    in this order as dowk() needs doyr()
  386.    */
  387.   
  388.   doyr(tp) ;
  389.   dowk(tp) ;
  390.   /*
  391.    * all done !
  392.    */
  393.   return(NULL) ;
  394.   badday :
  395.   tp->tm_mday = FALSE ;
  396.   return(sp) ;
  397.   badmon :
  398.   return(sp) ;
  399.   badyr :
  400.   tp->tm_year = FALSE ;
  401.   return(sp) ;
  402. }
  403.  
  404. /*
  405.  *  gtime() -- get  hh:mm:ss or equivalent into a tm struct.
  406.  */
  407. char *
  408. gtime(sp,tp) register char *sp ; register struct tm *tp ;
  409. {
  410.   char buf[MAXTOK],*cp ;
  411.   
  412.   sp = skipw(sp) ;
  413.   if(isdigit(*sp))
  414.     {
  415.       sp = dstring(buf,sp) ;
  416.       tp->tm_hour = atoi(buf) ;
  417.       sp = skipw(sp) ;
  418.       if(*sp == ':') sp = skipw(++sp) ;
  419.       else goto out ;
  420.       if(isdigit(*sp))
  421.     {
  422.       sp = dstring(buf,sp) ;
  423.       tp->tm_min = atoi(buf) ;
  424.       sp = skipw(sp) ;
  425.       if(*sp == ':') sp = skipw(++sp) ;
  426.       else goto out ;
  427.       if(isdigit(*sp))
  428.         {
  429.           sp = dstring(buf,sp) ;
  430.           tp->tm_sec = atoi(buf) ;
  431.         }
  432.     }
  433.     }
  434.   out :
  435.   sp = skipw(sp) ;
  436.   if(isalpha(*sp))    /* PM:AM or time zone or both */
  437.     {
  438.       cp = extract(buf,sp) ;
  439.       if(strcmp(buf,"am") == 0 || strcmp(buf,"pm") == 0)
  440.     {
  441.       if(buf[0] == 'p' && tp->tm_hour < 12)
  442.         tp->tm_hour += 12 ;
  443.       sp = cp = skipw(cp) ;
  444.       cp = extract(buf,cp) ;
  445.     }
  446.       if(lookup(buf,tz))
  447.     {
  448.       if(buf[1] == 'd') tp->tm_isdst++ ;
  449.       sp = skipw(cp) ;
  450.     }
  451.     }
  452.   return (sp);    
  453. }
  454.  
  455. /*
  456.  * days() -- how many days were in a year.
  457.  *
  458.  *    Ok, you were all wondering so here it is folks...
  459.  *    THE LEAP YEAR CALCULATION
  460.  *    note: does not take into account 1752.
  461.  */
  462. days(y) register int y ;
  463. {
  464.   if(y < 1970) y += 1900 ;
  465.   if(((y % 4) == 0) && ( (y % 100) || ((y % 400)==0) )) y = 366 ;
  466.   else y = 365 ;
  467.   return(y) ;
  468. }
  469.  
  470.  
  471. /*
  472.  * jan1() -- return day of the week of jan 1 of given year
  473.  */
  474. jan1(yr)
  475. {
  476.   register y, d;
  477.   
  478.   /*
  479.    *    normal gregorian calendar
  480.    *    one extra day per four years
  481.    */
  482.   
  483.   y = yr;
  484.   d = 4+y+(y+3)/4;
  485.   
  486.   /*
  487.    *    julian calendar
  488.    *    regular gregorian
  489.    *    less three days per 400
  490.    */
  491.   
  492.   if(y > 1800) {
  493.     d -= (y-1701)/100;
  494.     d += (y-1601)/400;
  495.   }
  496.   
  497.   /*
  498.    *    take care of weirdness at 1752.
  499.    */
  500.   
  501.   if(y > 1752)
  502.     d += 3;
  503.   
  504.   return(d%7);
  505. }
  506.  
  507. /*
  508.  * dowk() -- insert day of week given year and day of year into tm struct.
  509.  */
  510. dowk(tp) register struct tm *tp ;
  511. {
  512.   tp->tm_wday = (jan1(tp->tm_year+1900) + tp->tm_yday) % 7 ;
  513. }
  514.  
  515. /*
  516.  * doyr() -- insert partial day of year given yr and mon into tm struct.
  517.  */
  518. doyr(tp) register struct tm *tp ;
  519. {
  520.   register int i,j ;
  521.  
  522.   j = ((tp->tm_mon > 1) && (days(tp->tm_year) == 366)) ? 1 : 0 ;
  523.   for(i=1 ; i < tp->tm_mon ; i++)
  524.     j += mdays[i-1] ;
  525.   tp->tm_yday = j + tp->tm_mday - 1 ;
  526. }
  527.  
  528. /*----------------------------------------------------------------
  529.  *
  530.  * tm_to_time related
  531.  *
  532.  */
  533.  
  534. /*
  535.  * leap() -- Return 1 if `y' is a leap year, 0 otherwise.
  536.  */
  537. static int
  538. leap (y)
  539.      int y;
  540. {
  541.   y += 1900;
  542.   if (y % 400 == 0)
  543.     return (1);
  544.   if (y % 100 == 0)
  545.     return (0);
  546.   return (y % 4 == 0);
  547. }
  548.  
  549. /*
  550.  * ndays() -- number of days since UNIX epoch and time in a tm struct.
  551.  */
  552. static int
  553. ndays (p)
  554.      struct tm *p;
  555. {
  556.   register n = p->tm_mday;
  557.   register m, y;
  558.   register char *md = "\37\34\37\36\37\36\37\37\36\37\36\37";
  559.   
  560.   for (y = 70; y < p->tm_year; ++y) {
  561.     n += 365;
  562.     if (leap (y)) ++n;
  563.   }
  564.   for (m = 0; m < p->tm_mon; ++m)
  565.     n += md[m] + (m == 1 && leap (y));
  566.   return (n);
  567. }
  568.  
  569. /*
  570.  * tm_to_time() -- Convert a tm struct to a time_t.
  571.  *
  572.  * returns 0 if the time is before the UNIX epoch, 1/1/70 00:00:00
  573.  */
  574. time_t
  575. tm_to_time (tp)
  576.      struct tm *tp;
  577. {
  578.   register int m1, m2;
  579.   time_t t;
  580.   struct tm otm;
  581.   
  582.   /* special case date before epoch */
  583.   if( tp->tm_year < 70) return(0);
  584.   
  585.   t = (ndays (tp) - 1) * 86400L + tp->tm_hour * 3600L
  586.     + tp->tm_min * 60 + tp->tm_sec;
  587.   /*
  588.    * Now the hard part -- correct for the time zone:
  589.    */
  590.   otm = *tp;
  591.   tp = localtime (&t);
  592.   m1 = tp->tm_hour * 60 + tp->tm_min;
  593.   m2 = otm.tm_hour * 60 + otm.tm_min;
  594.   t -= ((m1 - m2 + 720 + 1440) % 1440 - 720) * 60L;
  595.   return (t);
  596. }
  597.  
  598. /*----------------------------------------------------------------
  599.  *
  600.  * Test program related
  601.  *
  602.  */
  603.  
  604. #if DEBUG
  605. /*
  606.  *  test driver
  607.  *    translates first arg from command line (argv[1])
  608.  *    and dumps out structure built.
  609.  */
  610. usage(sp) char *sp ;
  611. {
  612.   fprintf(stderr,"Usage: %s date\n",sp) ;
  613.   exit(1) ;
  614. }
  615.  
  616. /*
  617.  * main() -- test the gdate and tm_to_time routines
  618.  */
  619. main(argc,argv) int argc ; char **argv ;
  620. {
  621.   char *cp ;
  622.   struct tm tm ;
  623.   time_t t,t2 ;
  624.   char *asctime();
  625.   char *ctime();
  626.   
  627.   if(argc != 2) usage(*argv) ;
  628.   
  629.   if((cp = gdate(*++argv,&tm)) != NULL)
  630.     printf("error: %s (%s)\n",*argv,cp) ;
  631.   
  632.   printf("year : %d month: %d day: %d\n",
  633.      tm.tm_year,tm.tm_mon,tm.tm_mday);
  634.   printf("day of month: %d hour: %d minute: %d second: %d\n",
  635.      tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec) ;
  636.   printf("day of year: %d day of week: %d dst: %d\n",
  637.      tm.tm_yday, tm.tm_wday, tm.tm_isdst) ;
  638.   
  639.   t = time(NULL) ;
  640.   t2 = tm_to_time(&tm) ;
  641.   
  642.   printf("time_t of now %d, of arg %d\n",t, t2 ) ;
  643.   printf("Now:  %s", ctime(&t) );
  644.   printf("Arg:  %s", ctime(&t2) );
  645.   exit(0) ;
  646. }
  647.  
  648. #endif    /* DEBUG */
  649.  
  650.