home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume20 / deliver2.0 / part04 / unctime.y < prev    next >
Text File  |  1989-10-15  |  11KB  |  524 lines

  1. /*
  2.  * $Header: unctime.y,v 2.1 89/06/09 12:25:42 network Exp $
  3.  *
  4.  * Conversion of ctime-style date string back to a time_t.
  5.  * This module is in a different style from the rest of deliver
  6.  * because Chip Salzenberg didn't write it.
  7.  *
  8.  * $Log:    unctime.y,v $
  9.  * Revision 2.1  89/06/09  12:25:42  network
  10.  * Update RCS revisions.
  11.  * 
  12.  * Revision 1.3  89/06/09  12:24:01  network
  13.  * Baseline for 2.0 release.
  14.  * 
  15.  */
  16.  
  17. /* time_t
  18.    unctime(s)
  19.       char *s;
  20.  
  21. Convert s, which may be in almost any reasonable date format, to
  22. a time_t integer suitable for consumption by ctime(3).  Coincidentally
  23. destroys the contents of s.  Return -1 if s is not a recognizable legal date.
  24.  
  25. Any parts of the time left unspecified take on the current values.
  26.  
  27. "4 CST + 23[:10]" adds 23 minutes and optionally 10 seconds to the correction.
  28. "# nnnnnn" forces exactly nnnnnn seconds GMT since Jan. 1, 1970.
  29.   
  30. Copyright 1988, Michael J. Haertel.  Use this routine at your own risk.
  31. You may redistribute verbatim copies of this file.  You may redistribute
  32. modified versions of this file so long as (1) you state who last changed
  33. it, and (2) this copyright notice appears unmodified.
  34.  
  35. Some debugging by W. Anthony Smith.
  36.  
  37. Bug fix, minor enhancements, and non-BSD modifications by David MacKenzie.
  38.  
  39. Several changes by Chip Salzenberg for use with deliver:
  40.     Include "deliver.h".
  41.     Handle military timezones as per RFC822.
  42.     Elimination of "#if FTIME" in favor of "#if !USG".
  43.     Don't modify input string.
  44.     Consider extra junk in date string an error.
  45. */
  46.  
  47. %{
  48. #include "deliver.h"
  49. #include <ctype.h>
  50. #ifdef BSD
  51. # include <sys/time.h>
  52. #else
  53. # include <time.h>
  54. # ifndef USG
  55. #  include <sys/timeb.h>
  56. # endif
  57. #endif
  58.  
  59. extern long atol();
  60.  
  61. /* Delta is correction to turn specified time into GMT. */
  62. /* if (zoneflag), a timezone was explicitly specified. */
  63. static year, month, day, hour, minute, second, delta, zoneflag, errorflag, iflag;
  64. static long iresult;
  65.  
  66. #define YYSTYPE long
  67. %}
  68.  
  69. %token NUM MONTH AM PM
  70.  
  71. %%
  72.  
  73. date:
  74.   day time year
  75.   | day year time
  76.   | time day year
  77.   | time day
  78.   | day time
  79.   | day year
  80.   | day
  81.   | time
  82.   | '#' NUM        { iflag = TRUE; iresult = $2; }
  83.   ;            /* previous line forces exact time in seconds GMT */
  84.  
  85. day:
  86.   NUM MONTH        { month = $2; day = $1; }
  87.   | MONTH NUM        { month = $1; day = $2; }
  88.   | NUM '/' NUM        { month = $1; day = $3; }
  89.   ;
  90.  
  91. year:
  92.   ',' NUM        { year = $2; }
  93.   | '/' NUM        { year = $2; }
  94.   | NUM            { year = $1; }
  95.   ;
  96.  
  97. time:
  98.   clock AM        { hour %= 12; }
  99.   | clock PM        { hour = hour % 12 + 12; }
  100.   | clock
  101.   ;
  102.  
  103. clock:
  104.   NUM ':' NUM ':' NUM    { hour = $1; minute = $3; second = $5; }
  105.   | NUM ':' NUM        { hour = $1; minute = $3; }
  106.   ;
  107.  
  108. %%
  109.  
  110. /* Return true if s is a prefix of t; e.g. prefix("mar", "march") = true.
  111.    Note that comparison is case-insensitive. */
  112. static
  113. prefix(s,t)
  114.      char *s, *t;
  115. {
  116.   while (*s && *t && tolower(*s) == tolower(*t))
  117.     s++, t++;
  118.   return *s == 0;
  119. }
  120.  
  121. static char *lexptr;
  122.  
  123. static void
  124. initlex(s)
  125.      char *s;
  126. {
  127.   lexptr = s;
  128. }
  129.  
  130. static char *
  131. months[] =
  132. {
  133.   "jan",
  134.   "feb",
  135.   "mar",
  136.   "apr",
  137.   "may",
  138.   "jun",
  139.   "jul",
  140.   "aug",
  141.   "sep",
  142.   "oct",
  143.   "nov",
  144.   "dec",
  145.   0
  146. };
  147.  
  148. struct zonename
  149. {
  150.   char *name;            /* Name of the time zone. */
  151.   int delta;            /* Correction to add to GMT (in minutes) */
  152. };
  153.  
  154. static struct zonename zones[] =
  155. {
  156.   "gmt", 0,
  157.   "ut",  0,
  158.   "est", -5 * 60,       /* North American time zones */
  159.   "edt", -6 * 60,
  160.   "cst", -6 * 60,
  161.   "cdt", -7 * 60,
  162.   "mst", -7 * 60,
  163.   "mdt", -8 * 60,
  164.   "pst", -8 * 60,
  165.   "pdt", -9 * 60,
  166.   "z",   0,             /* Military time zones */
  167.   "a",   -1 * 60,
  168.   "b",   -2 * 60,
  169.   "c",   -3 * 60,
  170.   "d",   -4 * 60,
  171.   "e",   -5 * 60,
  172.   "f",   -6 * 60,
  173.   "g",   -7 * 60,
  174.   "h",   -8 * 60,
  175.   "i",   -9 * 60,
  176.   "k",   -10 * 60,
  177.   "l",   -11 * 60,
  178.   "m",   -12 * 60,
  179.   "n",   1 * 60,
  180.   "o",   2 * 60,
  181.   "p",   3 * 60,
  182.   "q",   4 * 60,
  183.   "r",   5 * 60,
  184.   "s",   6 * 60,
  185.   "t",   7 * 60,
  186.   "u",   8 * 60,
  187.   "v",   9 * 60,
  188.   "w",   10 * 60,
  189.   "x",   11 * 60,
  190.   "y",   12 * 60,
  191.   0, 0
  192. };
  193.  
  194. /* Lexical analyzer.  Gather alphabetics into tokens; if they are unknown
  195.    strings ignore them, and if they are months return the appropriate value.
  196.    If the token is the name of the time zone set delta = correction and
  197.    zoneflag = TRUE, and skip ahead to the next token (the parser itself
  198.    never sees time zones).
  199.    If the token is a number, return its value.
  200.    If it is a punctuation mark, return the character code.
  201.    Ignore white space.  */
  202. static
  203. yylex()
  204. {
  205.   register i;
  206.   char token[40];    /* Probably paranoid. */
  207.   
  208.   for (;;)
  209.     {
  210.       while (isspace(*lexptr))
  211.     lexptr++;
  212.       if (*lexptr == 0)
  213.     return 0;
  214.       else if (isalpha(*lexptr))
  215.     {
  216.       i = 0;
  217.       while (isalpha(*lexptr))
  218.         token[i++] = *lexptr++;    /* Null termination is automatic. */
  219.       for (i = 0; months[i]; i++)
  220.         if (prefix(months[i],token))
  221.           {
  222.         yylval = i + 1;
  223.         return MONTH;
  224.           }
  225.       for (i = 0; zones[i].name; i++)
  226.         if (prefix(zones[i].name,token))
  227.           {
  228.         int oper, next;
  229.  
  230.         zoneflag = TRUE;
  231.         delta = zones[i].delta;
  232.         oper = yylex();
  233.         /* Syntax: "4 CST + 23[:10]" adds 23 minutes and
  234.         optionally 10 seconds to delta (the correction). */
  235.         if (oper == '+' || oper == '-')
  236.           {
  237.             (void) yylex();
  238.             delta += (oper == '+' ? 60 : -60) * yylval;
  239.             next = yylex();
  240.             if (next == ':')
  241.               {
  242.             (void) yylex();
  243.             delta += (oper == '+' ? 1 : -1) * yylval;
  244.               }
  245.             else
  246.               return next;
  247.           }
  248.         else
  249.           return oper;
  250.           }
  251.       if (prefix("pm",token) || prefix("p.m.", token))
  252.         return PM;
  253.       if (prefix("am",token) || prefix("a.m.", token))
  254.         return AM;
  255.       continue;
  256.     }
  257.       else if (isdigit(*lexptr))
  258.     {
  259.       i = 0;
  260.       while (isdigit(*lexptr))
  261.         token[i++] = *lexptr++;
  262.       token[i] = '\0';
  263.       yylval = atoi(token);
  264.       return NUM;
  265.     }
  266.       else
  267.     return *lexptr++;
  268.     }
  269. }
  270.  
  271. /* ARGSUSED */
  272. static
  273. yyerror(s)
  274.      char *s;
  275. {
  276.   errorflag = TRUE;
  277. }
  278.  
  279. /* Is y a leap year? */
  280. #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
  281.  
  282. /* Number of leap years from 1970 to y (not including y itself) */
  283. #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
  284.  
  285. /* This macro returns the "day" number of the sunday immediately
  286.    preceding or equal to the argument in the current year. */
  287. #define FIRST_SUNDAY 3
  288. #define dayofepoch(day) ((day) + (year - 1970) * 365 + nleap(year))
  289. #define sunday(day)  ((day) - (dayofepoch(day) + 7 - FIRST_SUNDAY) % 7)
  290.  
  291. /* correction()
  292.    returns the daylight savings correction in seconds to ADD to GMT
  293.    to get correct local time.
  294.    Since we are converting local back to GMT, we SUBTRACT this later on
  295.    (local = gmt + correction(); gmt = local - correction()).
  296.  
  297.    While we're at it, we also add the longitude correction for minutes
  298.    west of Greenwich.  To do this, we have all these fascinating tables
  299.    here . . .  */
  300.  
  301. #ifdef BSD
  302.  
  303. struct dstinfo
  304. {
  305.   int year;            /* Info is for this year, or default if zero. */
  306.   int start;            /* DST begins sunday before this day. */
  307.   int end;            /* DST ends sunday before this day. */
  308. };
  309.  
  310. /* USA. */
  311. static struct dstinfo
  312. usa_dst[] =
  313. {
  314.   1974, 5, 333,
  315.   1975, 58, 303,
  316.   0, 119, 303
  317. };
  318.  
  319. /* Australia. */
  320. static struct dstinfo
  321. aus_dst[] =
  322. {
  323.   1970, 999, 0,
  324.   1971, 303, 0,
  325.   1972, 303, 58,
  326.   0, 303, 65
  327. };
  328.  
  329. /* Western Europe. */
  330. static struct dstinfo
  331. weur_dst[] =
  332. {
  333.   1983, 89, 296,
  334.   0, 89, 303
  335. };
  336.  
  337. /* Middle Europe (also used for Eastern Europe, for lack of better
  338.    information). */
  339. static struct dstinfo
  340. meur_dst[] =
  341. {
  342.   1983, 89, 296,
  343.   0, 89, 272
  344. };
  345.  
  346. /* Canada is same as US, except no early 70's insanity. */
  347. static struct dstinfo
  348. can_dst[] =
  349. {
  350.   0, 119, 303
  351. };
  352.  
  353. struct dst_rules
  354. {
  355.   int magic;            /* Gettimeofday magic number for rule type */
  356.   struct dstinfo *entry;    /* Pointer to struct dstinfo array. */
  357.   int correction;        /* Correction in minutes to GMT. */
  358. };
  359.  
  360. static struct dst_rules
  361. dstrules[] =
  362. {
  363.   DST_USA, usa_dst, 60,
  364.   DST_AUST, aus_dst, -60,    /* Southern hemisphere */
  365.   DST_WET, weur_dst, 60,
  366.   DST_MET, meur_dst, 60,
  367.   DST_EET, meur_dst, 60,
  368.   DST_CAN, can_dst, 60,
  369.   -1, 0, 0
  370. };
  371.  
  372. static
  373. correction(day,tz)
  374.      int day;                /* Day number in current year.  */
  375.      struct timezone *tz;
  376. {
  377.   int i, correc = 0;
  378.   struct dstinfo *dst;
  379.   
  380.   /* Did the user specify in the input string a timezone correction to use? */
  381.   if (zoneflag)
  382.     return delta * 60;
  383.  
  384.   /* Since no correction was explicitly specified, we use local time zone and
  385.      DST, as returned by gettimeofday() earlier . . . */
  386.   if (tz->tz_dsttime)
  387.     for (i = 0; dstrules[i].magic != -1; i++)
  388.       if (dstrules[i].magic == tz->tz_dsttime)
  389.     {
  390.       dst = dstrules[i].entry;
  391.       while (dst->year != year && dst->year)
  392.         dst++;
  393.       if (sunday(dst->start) <= day && day <= sunday(dst->end)
  394.           /* For some reason, DST starts/ends at 2 am sunday mornings. */
  395.           && !(day == sunday(dst->start) && hour < 2)
  396.           && !(day == sunday(dst->end) && hour >= 2))
  397.         correc = dstrules[i].correction;
  398.       break;
  399.     }
  400.   correc -= tz->tz_minuteswest;
  401.   return correc * 60;
  402. }
  403.  
  404. #else /* !BSD */
  405.  
  406. static
  407. correction()
  408. {
  409. #ifdef USG
  410.   extern long timezone;
  411. #else
  412.   struct timeb tb;
  413. #endif
  414.   
  415.   /* Did the user specify in the input string a timezone correction to use? */
  416.   if (zoneflag)
  417.     return delta * 60;
  418.  
  419.   /* Since no correction was explicitly specified, we use local time zone. */
  420. #ifdef USG
  421.   tzset();
  422.   return (int) -timezone;
  423. #else
  424.   ftime(&tb);
  425.   return tb.timezone * -60;
  426. #endif
  427. }
  428.  
  429. #endif /* !BSD */
  430.  
  431. static short
  432. monthlens[] =
  433. {
  434.   31,                /* January */
  435.   28,                /* February */
  436.   31,                /* March */
  437.   30,                /* April */
  438.   31,                /* May */
  439.   30,                /* June */
  440.   31,                /* July */
  441.   31,                /* August */
  442.   30,                /* September */
  443.   31,                /* October */
  444.   30,                /* November */
  445.   31                /* December */
  446. };
  447.  
  448. time_t
  449. unctime(s)
  450.      char *s;
  451. {
  452. #ifdef BSD
  453.   struct timeval tv;
  454.   struct timezone tz;
  455. #else
  456.   time_t now;
  457. #endif
  458.   struct tm *tm;
  459.   int dayofyear;
  460.  
  461. #ifdef BSD
  462.   (void) gettimeofday(&tv,&tz);
  463.   /* The cast is required to shut lint up.  Berkeley goes to all the effort
  464.      to define time_t, why don't they use it? */
  465.   tm = localtime(&(time_t) tv.tv_sec);
  466. #else
  467.   (void) time(&now);
  468.   tm = localtime(&now);
  469. #endif
  470.   year = tm->tm_year;
  471.   month = tm->tm_mon + 1;
  472.   day = tm->tm_mday;
  473.   hour = tm->tm_hour;
  474.   minute = tm->tm_min;
  475.   second = tm->tm_sec;
  476.   zoneflag = FALSE;
  477.   errorflag = FALSE;
  478.  
  479.   initlex(s);
  480.   (void) yyparse();
  481.  
  482.   if (errorflag)
  483.     return -1;
  484.  
  485.   /* If garbage beyond valid date, that's an error. */
  486.   while (*lexptr && isspace(*lexptr))
  487.     ++lexptr;
  488.   if (*lexptr)
  489.      return -1;
  490.  
  491.   /* User forced the exact time in seconds GMT, no further work necessary. */
  492.   if (iflag)
  493.     return iresult;
  494.  
  495.   /* Try to keep the year reasonable (i.e., within the domain of ctime()). */
  496.   if (year < 1970)
  497.     year += 1900;
  498.   if (year < 1970)
  499.     year += 100;
  500.  
  501.   /* Check for preposterous months/days/times. */
  502.   if (month < 1 || month > 12 || day < 1 ||
  503.       day > monthlens[month - 1] && !(month == 2 && day == 29 && leap(year))
  504.       || hour > 23 || minute > 59 || second > 59)
  505.     return -1;
  506.  
  507.   /* Mostly for convenience in sunday() macro, we use zero-origin days. */
  508.   dayofyear = day - 1;
  509.   if (month > 2 && leap(year))
  510.     ++dayofyear;
  511.   while (--month > 0)
  512.     dayofyear += monthlens[month - 1];
  513.  
  514.   /* Wow! */
  515.   return 86400 * (dayofyear + 365 * (year - 1970) + nleap(year))
  516.     + 3600 * hour + 60 * minute + second
  517. #ifdef BSD
  518.     - correction(dayofyear,&tz)
  519. #else
  520.     - correction()
  521. #endif
  522.     ;
  523. }
  524.