home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / unix_c / languags / c / gdate.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-03-21  |  10.6 KB  |  520 lines

  1.  5-Jul-85 20:17:20-MDT,11280;000000000001
  2. Return-Path: <unix-sources-request@BRL.ARPA>
  3. Received: from BRL-TGR.ARPA by SIMTEL20.ARPA with TCP; Fri 5 Jul 85 20:12:17-MDT
  4. Received: from usenet by TGR.BRL.ARPA id a014714; 5 Jul 85 21:41 EDT
  5. From: Barry Shein <root@bu-cs.uucp>
  6. Newsgroups: net.sources
  7. Subject: Re: date <-> day-number software sought
  8. Message-ID: <467@bu-cs.UUCP>
  9. Date: 5 Jul 85 23:26:03 GMT
  10. To:       unix-sources@BRL-TGR.ARPA
  11.  
  12. Here's a string->tm converter that I wrote quite some time ago
  13. that takes several forms of date input and heuristically tries
  14. to figure out what it might be, more or less inspired by TOPS-20
  15. (handles more date formats I think.)
  16.  
  17. This is what date(1) should have been built out of!
  18.  
  19. It's just one file, the comments should be enough to figure out
  20. what is going on (very simple to interface, probably hairy to
  21. modify tho.) Put it into a file called gdate.c
  22.  
  23.     -Barry Shein, Boston University
  24.  
  25. ----------------shear here------------------
  26. #include <stdio.h>
  27. #include <ctype.h>
  28. /*
  29.  *    in 4.2bsd they moved time.h to <sys/time.h>
  30.  */
  31. #define BSD42
  32. #ifdef BSD42
  33. #include <sys/time.h>
  34. #else
  35. #include <time.h>
  36. #endif
  37. #define MAXTOK 20
  38. /*
  39.  * routines to turn a date from various formats to other formats
  40.  *
  41.  *    Primary interesting routine is gdate() which eats a date
  42.  *    string of several common formats (see comment) and
  43.  *    fills in a standard UNIX tm structure.
  44.  **    Barry Shein, Boston University
  45.  *    if you compile it -DDEBUG (with DEBUG defined) it will
  46.  *    pull in a main() routine to run standalone for testing.
  47.  */
  48. #define AMBIG  -1    /* ambiguous month */
  49. #define FALSE  -2    /* bad syntax       */
  50. char *months[] = {
  51.     "january", "february", "march", "april", "may", "june", "july",
  52.     "august", "september", "october", "november", "december", 0
  53. } ;
  54. char *dow[] = {
  55.     "sunday", "monday", "tuesday", "wednesday", "thursday",
  56.     "friday", "saturday", 0
  57. } ;
  58. /*
  59.  *    known time-zone name list
  60.  */
  61. char *tz[] = 
  62. {
  63.     "adt", "ast", "edt", "est", "cdt", "cst", "mst", "mdt",
  64.     "pdt", "pst", "gmt",
  65.     0
  66. } ;
  67. char mdays[] = {
  68.     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  69. } ;
  70. lcase(c) register char c ;
  71. {
  72.     return(isupper(c) ? tolower(c) : c) ;
  73. }
  74. /*
  75.  *  get a digit string from sp into bp (buffer) returning new sp
  76.  */
  77. char *
  78. dstring(bp,sp) register char *sp, *bp  ;
  79. {
  80.     register int i = 0 ;
  81.     while(isdigit(*sp))
  82.         if(i++ == MAXTOK) break ;
  83.         else *bp++ = *sp++ ;
  84.     *bp = '\0' ;
  85.     return(sp) ;
  86. }
  87. /*
  88.  *  skip white space returning updated ptr
  89.  */
  90. char *
  91. skipw(sp) register char *sp ;
  92. {
  93.     while(isspace(*sp) || *sp == '-') ++sp ;
  94.     return(sp) ;
  95. }
  96. /*
  97.  *  return how many chars of s1 prefix s2
  98.  */
  99. prefix(s1,s2) register char *s1, *s2 ;
  100. {
  101.     register int i = 0  ;
  102.  
  103.     for(; *s1 == *s2 ; s1++,s2++,i++)
  104.         if(*s2 == '\0') break ;
  105.     return(*s1 == '\0' ? i : 0) ;
  106. }
  107. /*
  108.  *  look up str in list for non-ambiguous (prefix) match
  109.  */
  110. find(sp,lp) register char *sp, **lp ;
  111. {
  112.     int i,j,ambig = 0 ;
  113.     register int k ;
  114.     int ret  = FALSE ;
  115.  
  116.     for(i = 0,k = 0 ; *lp ; lp++,k++)
  117.         if((j  = prefix(sp,*lp)) > i)
  118.         {
  119.             ambig = 0 ;
  120.             i = j ;
  121.             ret = k + 1 ;
  122.         }
  123.         else if(j && (i == j)) ++ambig ;
  124.     return(ambig ? AMBIG : ret) ;
  125. }
  126. /*
  127.  * like find but demands exact match
  128.  */
  129. lookup(sp,lp) register char *sp, **lp ;
  130. {
  131.     int i = 0 ;
  132.  
  133.     for(i=0 ; *lp ; lp++,i++)
  134.         if(strcmp(sp,*lp) == 0) return(i+1) ;
  135.     return(0) ;
  136. }
  137. /*
  138.  * extract a token
  139.  */
  140. char *
  141. extract(bp,sp) register char *bp, *sp ;
  142. {
  143.     register int i = 0 ;
  144.  
  145.     sp = skipw(sp) ;
  146.     for(; isalnum(*sp); sp++)
  147.         if(i++ == MAXTOK) break ;
  148.         else *bp++ = lcase(*sp) ;
  149.     *bp = '\0' ;
  150.     return(sp) ;
  151. }
  152. /*
  153.  *  fill up an area with a value (eg. zeros)
  154.  */
  155. fill(cp,c,n) register char *cp, c ; register int n ;
  156. {
  157.     while(n--) *cp++ = c ;
  158. }
  159.  
  160. char *gdate(), *gtime() ;
  161. long totime() ;
  162. #if DEBUG
  163. /*
  164.  *  test driver
  165.  *    translates first arg from command line (argv[1])
  166.  *    and dumps out structure built.
  167.  */
  168. usage(sp) char *sp ;
  169. {
  170.     fprintf(stderr,"Usage: %s date\n",sp) ;
  171.     exit(1) ;
  172. }
  173. main(argc,argv) int argc ; char **argv ;
  174. {
  175.     char *cp ;
  176.     struct tm tm ;
  177.     long t,t2 ;
  178.  
  179.     if(argc != 2) usage(*argv) ;
  180.     if((cp = gdate(*++argv,&tm)) != NULL)
  181.         printf("error: %s (%s)\n",*argv,cp) ;
  182.     printf("year : %d month: %d day: %d\n",
  183.         tm.tm_year,tm.tm_mon,tm.tm_mday);
  184.     printf("day of month: %d hour: %d minute: %d second: %d\n",
  185.         tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec) ;
  186.     printf("day of year: %d day of week: %d dst: %d\n",
  187.         tm.tm_yday, tm.tm_wday, tm.tm_isdst) ;
  188.     time(&t) ;
  189.     t2 = totime(&tm) ;
  190.     printf("%D %D %D\n",t,t2,t2-t) ;
  191.     exit(0) ;
  192. }
  193. #endif    /* DEBUG */
  194. /*
  195.  *    gdate(date_str_ptr,time_buf_ptr)
  196.  *    (see CTIME(3) in UPM for second arg format)
  197.  *    takes many reasonable date strings and translates to
  198.  *    the time-buf structure (integers)
  199.  *
  200.  *    formats (eg.):
  201.  *        oct 19, 1983
  202.  *        OcT 19, 1983 12:43
  203.  *        oCt 19, 1983 12:43:33
  204.  *        oct 19 1983 ...
  205.  *        19 oct 83 ....
  206.  *        10/19/83 12:43:33
  207.  *        19-OCT-83 12:43:00 (DEC style)
  208.  *        Wed Oct 19 12:43 EST 83 (UNIX style)
  209.  *        oct. 19, 83 1:44 pm est
  210.  *        831019124333    (see date(1))
  211.  *    some variations on those themes also.
  212.  *
  213.  *    BUGS: probably a few (maybe some in the eye of the beholder)
  214.  *        does not set dst flag (unless 'EDT' is in string)
  215.  */
  216. char *
  217. gdate(sp,tp) register char *sp ; register struct tm *tp ;
  218. {
  219.     char buf[MAXTOK] ;
  220.  
  221.     fill(tp,'\0',sizeof *tp) ;
  222.     sp = skipw(sp) ;
  223.     if(isdigit(*sp))    /* either '7/12/83' or '12 jul 83' */
  224.     {
  225.         if(isdigit(sp[1]) && isdigit(sp[2])) /* UNIX yymmddhhmmss */
  226.         {
  227.             buf[2] = '\0' ;
  228.             strncpy(buf,sp,2) ;
  229.             tp->tm_year = atoi(buf) ;
  230.             strncpy(buf,sp += 2,2) ;
  231.             tp->tm_mon = atoi(buf) ;
  232.             sp += 2 ;
  233.             if(!isdigit(*sp)) goto badday ;
  234.             strncpy(buf,sp,2) ;
  235.             tp->tm_mday = atoi(buf) ;
  236.             sp += 2 ;
  237.             if(!isdigit(*sp)) goto check ;
  238.             strncpy(buf,sp,2) ;
  239.             tp->tm_hour ;
  240.             sp += 2 ;
  241.             if(!isdigit(*sp)) goto check ;
  242.             strncpy(buf,sp,2) ;
  243.             tp->tm_min = atoi(buf) ;
  244.             sp += 2 ;
  245.             if(!isdigit(*sp)) goto check ;
  246.             strncpy(buf,sp,2) ;
  247.             tp->tm_min = atoi(buf) ;
  248.             goto check ;
  249.         }
  250.         sp = dstring(buf,sp) ;
  251.         sp = skipw(sp) ;
  252.         if(*sp == '/')    /* must be '7/12/83' */
  253.         {
  254.             if((tp->tm_mon = atoi(buf)) < 1 || (tp->tm_mon > 12))
  255.             {
  256.                 tp->tm_mon = FALSE ;
  257.                 goto badmon ;
  258.             }
  259.             sp = skipw(++sp) ;
  260.             if(!isdigit(*sp)) goto badday ;
  261.             sp = dstring(buf,sp) ;
  262.             tp->tm_mday = atoi(buf) ;
  263.  
  264.             sp = skipw(sp) ;
  265.             if(*sp != '/') goto badyr ;
  266.             sp = skipw(++sp) ;
  267.             if(!isdigit(*sp)) goto badyr ;
  268.             sp = dstring(buf,sp) ;
  269.             tp->tm_year = atoi(buf) ;
  270.  
  271.             sp = gtime(sp,tp) ;
  272.         }
  273.         else
  274.         {
  275.             /*
  276.              * must be '12 jul 83'
  277.              */
  278.             tp->tm_mday = atoi(buf) ;
  279.  
  280.             sp = extract(buf,sp) ;
  281.             if((tp->tm_mon = find(buf,months)) < 1) goto badmon ;
  282.  
  283.             if(*sp == '.') ++sp ;
  284.             sp = skipw(sp) ;
  285.  
  286.             if(!isdigit(*sp)) goto badyr ;
  287.             sp = dstring(buf,sp) ;
  288.             tp->tm_year = atoi(buf) ;
  289.  
  290.             sp = gtime(sp,tp) ;
  291.         }
  292.     }
  293.     else
  294.     {
  295.         int flag = 0 ;    /* used to indicate looking for UNIX style */
  296.  
  297.         /*
  298.          * either 'jul 12 83' or (UNIX) Wed jul 12 18:33 EST 1983
  299.          */
  300.         sp = extract(buf,sp) ;
  301.         if(find(buf,dow) > 0)
  302.         {
  303.             sp = extract(buf,sp) ;
  304.             ++flag ;
  305.         }
  306.  
  307.         if((tp->tm_mon = find(buf,months)) < 1) goto badmon ;
  308.  
  309.         if(*sp == '.') ++sp ;
  310.         sp = skipw(sp) ;
  311.  
  312.         if(!isdigit(*sp)) goto badday ;
  313.         sp = dstring(buf,sp) ;
  314.         tp->tm_mday = atoi(buf) ;
  315.  
  316.         sp = skipw(sp) ;
  317.         if(*sp == ',') sp++ ;
  318.         sp = skipw(sp) ;
  319.  
  320.         if(flag) sp = skipw(gtime(sp,tp)) ;
  321.  
  322.         if(!isdigit(*sp)) goto badyr ;
  323.         sp = dstring(buf,sp) ;
  324.         tp->tm_year = atoi(buf) ;
  325.  
  326.         if(!flag) sp = gtime(sp,tp) ;
  327.     }
  328. check:
  329.     /*
  330.      * check for ridiculous numbers
  331.      */
  332.     if(tp->tm_mday < 1) goto badday ;
  333.     if(tp->tm_mday > mdays[tp->tm_mon-1])
  334.         if(!((tp->tm_mon == 2) &&    /* check for Feb 29 */
  335.             (tp->tm_mday == 29) && (days(tp->tm_year) == 365) ))
  336.                 goto badday ;
  337.     if(tp->tm_year >= 1900) tp->tm_year -= 1900 ;
  338.     if(tp->tm_hour > 23)
  339.     {
  340.         tp->tm_hour = FALSE ;
  341.         return(sp) ;
  342.     }
  343.     if(tp->tm_min > 59)
  344.     {
  345.         tp->tm_hour = FALSE ;
  346.         return(sp) ;
  347.     }
  348.     if(tp->tm_sec > 59)
  349.     {
  350.         tp->tm_sec = FALSE ;
  351.         return(sp) ;
  352.     }
  353.     /*
  354.      *    fill in day of year, day of week (these calls must be
  355.      *    in this order as dowk() needs doyr()
  356.      */
  357.  
  358.     doyr(tp) ;
  359.     dowk(tp) ;
  360.     /*
  361.      * all done !
  362.      */
  363.     return(NULL) ;
  364. badday :
  365.     tp->tm_mday = FALSE ;
  366.     return(sp) ;
  367. badmon :
  368.     return(sp) ;
  369. badyr :
  370.     tp->tm_year = FALSE ;
  371.     return(sp) ;
  372. }
  373. /*
  374.  *  get  hh:mm:ss
  375.  */
  376. char *
  377. gtime(sp,tp) register char *sp ; register struct tm *tp ;
  378. {
  379.     char buf[MAXTOK],*cp ;
  380.  
  381.     sp = skipw(sp) ;
  382.     if(isdigit(*sp))
  383.     {
  384.         sp = dstring(buf,sp) ;
  385.         tp->tm_hour = atoi(buf) ;
  386.         sp = skipw(sp) ;
  387.         if(*sp == ':') sp = skipw(++sp) ;
  388.         else goto out ;
  389.         if(isdigit(*sp))
  390.         {
  391.             sp = dstring(buf,sp) ;
  392.             tp->tm_min = atoi(buf) ;
  393.             sp = skipw(sp) ;
  394.             if(*sp == ':') sp = skipw(++sp) ;
  395.             else goto out ;
  396.             if(isdigit(*sp))
  397.             {
  398.                 sp = dstring(buf,sp) ;
  399.                 tp->tm_sec = atoi(buf) ;
  400.             }
  401.         }
  402.     }
  403. out :
  404.     sp = skipw(sp) ;
  405.     if(isalpha(*sp))    /* PM:AM or time zone or both */
  406.     {
  407.         cp = extract(buf,sp) ;
  408.         if(strcmp(buf,"am") == 0 || strcmp(buf,"pm") == 0)
  409.         {
  410.             if(buf[0] == 'p' && tp->tm_hour < 12)
  411.                 tp->tm_hour += 12 ;
  412.             sp = cp = skipw(cp) ;
  413.             cp = extract(buf,cp) ;
  414.         }
  415.         if(lookup(buf,tz))
  416.         {
  417.             if(buf[1] == 'd') tp->tm_isdst++ ;
  418.             sp = skipw(cp) ;
  419.         }
  420.     }
  421.     return(sp) ;
  422. }
  423. /*
  424.  *    Ok, you were all wondering so here it is folks...
  425.  *    THE LEAP YEAR CALCULATION
  426.  *    note: does not take into account 1752.
  427.  */
  428. days(y) register int y ;
  429. {
  430.     if(y < 1970) y += 1900 ;
  431.     if(((y % 4) == 0) && ( (y % 100) || ((y % 400)==0) )) y = 366 ;
  432.     else y = 365 ;
  433.     return(y) ;
  434. }
  435. #include <sys/types.h>
  436. #include <sys/timeb.h>
  437. /*
  438.  *    translate a time.h structure to seconds since 1/1/70
  439.  *    (ie. UNIX internal time format...GMT)
  440.  */
  441. long
  442. totime(tp) register struct tm *tp ;
  443. {
  444.     long ret = 0 ;
  445.     int i,j ;
  446.     struct timeb tb ;
  447.  
  448.     if(tp->tm_year < 70) return(0) ;
  449.     for(i=1970,j=tp->tm_year+1900 ; i<j ; i++)
  450.         ret += days(i) ;
  451.     for(i=1 ; i < tp->tm_mon ; i++)
  452.         ret += mdays[i-1] ;
  453.     if((days(tp->tm_year) == 366) && (tp->tm_mon > 2)) ++ret ;
  454.     ret += (tp->tm_mday - 1) ;
  455.     ret *= 24 ;
  456.     ret += tp->tm_hour ;
  457.     ret *= 60 ;
  458.     ftime(&tb) ;
  459.     ret += (tp->tm_min + tb.timezone) ;
  460.     ret *= 60 ;
  461.     ret += tp->tm_sec ;
  462.     return(ret) ;
  463. }
  464. /*
  465.  *    return day of the week
  466.  *    of jan 1 of given year
  467.  */
  468.  
  469. jan1(yr)
  470. {
  471.     register y, d;
  472.  
  473. /*
  474.  *    normal gregorian calendar
  475.  *    one extra day per four years
  476.  */
  477.  
  478.     y = yr;
  479.     d = 4+y+(y+3)/4;
  480.  
  481. /*
  482.  *    julian calendar
  483.  *    regular gregorian
  484.  *    less three days per 400
  485.  */
  486.  
  487.     if(y > 1800) {
  488.         d -= (y-1701)/100;
  489.         d += (y-1601)/400;
  490.     }
  491.  
  492. /*
  493.  *    take care of weirdness at 1752.
  494.  */
  495.  
  496.     if(y > 1752)
  497.         d += 3;
  498.  
  499.     return(d%7);
  500. }
  501. /*
  502.  *    given day of year and year calculate and insert day-of-week (0..6)
  503.  */
  504. dowk(tp) register struct tm *tp ;
  505. {
  506.     tp->tm_wday = (jan1(tp->tm_year+1900) + tp->tm_yday) % 7 ;
  507. }
  508. /*
  509.  *    calculate day of year from year
  510.  */
  511. doyr(tp) register struct tm *tp ;
  512. {
  513.     register int i,j ;
  514.  
  515.     j = ((tp->tm_mon > 2) && (days(tp->tm_year) == 366)) ? 1 : 0 ;
  516.     for(i=1 ; i < tp->tm_mon ; i++)
  517.         j += mdays[i-1] ;
  518.     tp->tm_yday = j + tp->tm_mday - 1 ;
  519. }
  520.