home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / nn.tar / nn-6.5.1 / pack_date.c < prev    next >
C/C++ Source or Header  |  1995-04-29  |  8KB  |  328 lines

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    Calculate an approximate "time_stamp" value for a date
  5.  *    string.  The actual value is not at all critical,
  6.  *    as long as the "ordering" is ok.
  7.  *
  8.  *    The result is NOT a time_t value, i.e. ctime() will
  9.  *    not produce the original Date string.
  10.  *
  11.  *    The date must have format:  [...,] [D]D Mmm YY hh:mm:ss TZONE
  12.  *
  13.  *    Thanks to Wayne Davison for the timezone decoding code.
  14.  */
  15.  
  16. #include "config.h"
  17.  
  18. /* pack_date.c */
  19.  
  20. static int tzone __APROTO((register char *date));
  21. static next_int __APROTO((char **dp));
  22.  
  23. /* #define DATE_TEST */ /* never define this !! */
  24.  
  25. #define NN_YEAR_ORIGIN 1987
  26.  
  27. #undef W
  28. #undef E
  29. #undef DST
  30. #undef UTC
  31. #define W    * (-60) - 
  32. #define E    * 60 +
  33. #define DST    + 60
  34. #define UTC    60 *
  35.  
  36. static struct zonetab {
  37.     char *tz_name;
  38.     int tz_offset;
  39. } ztab[] = {
  40.     "a",    UTC 1,        /* UTC+1h */
  41.     "acst",    9 E 30,        /* Cent. Australia */
  42.     "adt",    4 W 0 DST,    /* Atlantic (Canada) */
  43.     "aest",    10 E 0,        /* E. Australia */
  44.     "ast",    4 W 0,        /* Atlantic (Canada) */
  45.     "awst",    8 E 0,        /* W. Australia */
  46.     "b",    UTC 2,        /* UTC+2h */
  47.     "bst",    0 E 0 DST,    /* Great Britain summertime */
  48.     "c",    UTC 3,        /* UTC+3h */
  49.     "cdt",    6 W 0 DST,    /* Central */
  50.     "cest",    1 E 0 DST,    /* Central Europe */
  51.     "cet",    1 E 0,        /* Central Europe */
  52.     "cetdst",    1 E 0 DST,    /* Central Europe */
  53.     "cst",    6 W 0,        /* Central */
  54.     "d",    UTC 4,        /* UTC+4h */
  55.     "dnt",    1 E 0,        /* Denmark */
  56.     "dst",    1 E 0 DST,    /* Denmark */
  57.     "e",    UTC 5,        /* UTC+5h */
  58.     "edt",    5 W 0 DST,    /* Eastern US */
  59.     "eest",    2 E 0 DST,    /* Eastern Europe */
  60.     "eet",    2 E 0,        /* Eastern Europe */
  61.     "eetdst",    2 E 0 DST,    /* Eastern Europe */
  62.     "est",    5 W 0,        /* Eastern US */
  63.     "f",    UTC 6,        /* UTC+6h */
  64.     "g",    UTC 7,        /* UTC+7h */
  65.     "gmt",    0,        /*  */
  66.     "h",    UTC 8,        /* UTC+8h */
  67.     "hdt",    10 W 0 DST,    /* Hawaii/Alaska */
  68.     "hkt",    8 E 0,        /* Hong Kong */
  69.     "hst",    10 W 0,        /* Hawaii/Alaska */
  70.     "i",    UTC 9,        /* UTC+9h */
  71.     "ist",    2 E 0,        /* Israel */
  72.     "jst",    9 E 0,        /* Japan */
  73.     "k",    UTC 10,        /* UTC+10h */
  74.     "kdt",    9 E 0 DST,    /* Korea */
  75.     "kst",    9 E 0,        /* Korea */
  76.     "l",    UTC 11,        /* UTC+11h */
  77.     "m",    UTC 12,        /* UTC+12h */
  78.     "mdt",    7 W 0 DST,    /* Mountain US */
  79.     "mest",    1 E 0 DST,    /* Central Europe */
  80.     "met",    1 E 0,        /* Central Europe */
  81.     "metdst",    1 E 0 DST,    /* Central Europe */
  82.     "mst",    7 W 0,        /* Mountain */
  83.     "n",    UTC -1,        /* UTC-1h */
  84.     "ndt",    3 W 30 DST,    /* Nfld. (Canada) */
  85.     "nst",    3 W 30,        /* Nfld. (Canada) */
  86.     "nzdt",    12 E 0 DST,    /* New Zealand */
  87.     "nzst",    12 E 0,        /* New Zealand */
  88.     "o",    UTC -2,        /* UTC-2h */
  89.     "p",    UTC -3,        /* UTC-3h */
  90.     "pdt",    8 W 0 DST,    /* Pacific */
  91.     "pst",    8 W 0,        /* Pacific */
  92.     "q",    UTC -4,        /* UTC-4h */
  93.     "r",    UTC -5,        /* UTC-5h */
  94.     "s",    UTC -6,        /* UTC-6h */
  95.     "t",    UTC -7,        /* UTC-7h */
  96.     "u",    UTC -8,        /* UTC-8h */
  97.     "ut",    UTC 0,        /* UTC */
  98.     "utc",    UTC 0,        /* UTC */
  99.     "v",    UTC -9,        /* UTC-9h */
  100.     "w",    UTC -10,    /* UTC-10h */
  101.     "west",    0 E 0 DST,    /* Western Europe */
  102.     "wet",    0 E 0,        /* Western Europe */
  103.     "wetdst",    0 E 0 DST,    /* Western Europe */
  104.     "wst",    8 E 0,        /* Alternate W. Australia */
  105.     "x",    UTC -11,    /* UTC-11h */
  106.     "y",    UTC -12,    /* UTC-12h */
  107.     "ydt",    9 W 0 DST,    /* Yukon */
  108.     "yst",    9 W 0,        /* Yukon */
  109.     "z",    UTC 0,        /* UTC */
  110.     NULL,    0
  111. };
  112.  
  113. #undef MAXZ
  114. #define MAXZ    10
  115.  
  116. static int tzone(date)
  117. register char *date;
  118. {
  119.     register int i, n;
  120.     static char zone[MAXZ], num[MAXZ];
  121.     register struct zonetab *z;
  122.     int adjust, sign;
  123.  
  124.     i = 0;
  125.     while (*date && isascii(*date) && isspace(*date)) date++;
  126.     
  127.     for ( ; *date && isascii(*date) ; date++) {
  128.     if (*date == '+' || *date == '-' || isdigit(*date))
  129.         goto numeric_zone;
  130.     if (isspace(*date)) break;
  131.     if (!isalpha(*date)) continue;    /* p.s.t. -> pst */
  132.     if (i == MAXZ) continue;
  133.     zone[i++] = isupper(*date) ? tolower(*date) : *date;
  134.     }
  135.  
  136.     while (*date && isascii(*date) && isspace(*date)) date++;
  137.  
  138.     /* huh? */
  139.     if (*date && i < MAXZ-3) {
  140.     if (date[0] != 'D' && date[0] != 'd') goto no_dst;
  141.     if (date[1] != 'S' && date[1] != 's') goto no_dst;
  142.     if (date[2] != 'T' && date[2] != 't') goto no_dst;
  143.     zone[i++] = 'd';
  144.     zone[i++] = 's';
  145.     zone[i++] = 't';
  146.     }
  147.  
  148.  no_dst:
  149.     if (i == 0) return 0;
  150.     adjust = 0;
  151.     if (*date != '+' && *date != '-') goto non_numeric;
  152.  
  153.  numeric_zone:            /* {+-}[H]H[MM] */
  154.     switch (*date) {
  155.      case '-':
  156.     date++;
  157.     sign = -1;
  158.     break;
  159.      case '+':
  160.     date++;
  161.      default:
  162.     sign = 1;
  163.     break;
  164.     }
  165.  
  166.     adjust = 0;
  167.     for (n = 0; n < MAXZ && *date && isascii(*date) && isdigit(*date); )
  168.     num[n++] = *date++;
  169.     num[n] = NUL;
  170.  
  171.     switch (n) {
  172.      case 0:
  173.     break;
  174.      case 3:    /* +HMM */
  175.     adjust = atoi(num+1);
  176.      case 1:    /* +H */
  177.     adjust += (num[0] - '0') * 60;
  178.     break;
  179.      default:    /* +HHMM */
  180.     num[4] = NUL;
  181.     adjust = atoi(num + 2);
  182.     num[2] = NUL;
  183.      case 2:    /* +HH */
  184.     adjust += atoi(num) * 60;
  185.     break;
  186.     }
  187.     adjust *= sign;
  188.     if (i == 0) return adjust;
  189.  
  190.  non_numeric:
  191.     zone[i] = NUL;
  192.  
  193.     for (z = ztab; z->tz_name != NULL; z++) {
  194.     i = zone[0] - z->tz_name[0];
  195.     if (i == 0)        /* same first byte? */
  196.         i = strcmp(zone, z->tz_name);
  197.     if (i > 0)
  198.         continue;
  199.     if (i < 0)
  200.         break;
  201.     return z->tz_offset + adjust;
  202.     }
  203.     return adjust;
  204. }
  205.  
  206. static int
  207. next_int(dp)
  208. char **dp;
  209. {
  210.     register char *str = *dp;
  211.     register i = 0;
  212.  
  213.     while (*str && !isdigit(*str)) str++;
  214.  
  215.     while (*str && isdigit(*str))
  216.     i = (i * 10) + *str++ - '0';
  217.  
  218.     *dp = str;
  219.     return i;
  220. }
  221.  
  222. static int month_table[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
  223. #define leap_year(y) ((y)&3 == 0  &&  ((y)%100 != 0 || (y)%400 == 0))
  224.  
  225. static int
  226. month_days(year, mon)
  227. int year, mon;
  228. {
  229.     return month_table[mon] + (mon==1 && leap_year(year));
  230. }
  231.  
  232. time_stamp pack_date(date)
  233. char *date;
  234. {
  235.     register time_stamp res;
  236.     register int min, hour, day, mon, year;
  237.  
  238.     if (date == NULL || (day = next_int(&date)) == 0) return 0;
  239.  
  240.     while (*date && isspace(*date)) date++;
  241.  
  242.     if (date[0] == NUL || date[1] == NUL || date[2] == NUL) return 0;
  243.     switch (date[0]) {
  244.      case 'J': case 'j':
  245.     if (date[1] == 'a' || date[1] == 'A') { mon = 0; break; }
  246.     if (date[2] == 'n' || date[2] == 'N') { mon = 5; break; }
  247.     mon = 6; break;
  248.      case 'F': case 'f':
  249.     mon = 1; break;
  250.      case 'M': case 'm':
  251.     if (date[2] == 'r' || date[2] == 'R') { mon = 2; break; }
  252.     mon = 4; break;
  253.      case 'A': case 'a':
  254.     if (date[1] == 'p' || date[1] == 'P') { mon = 3; break; }
  255.     mon = 7; break;
  256.      case 'S': case 's':
  257.     mon = 8; break;
  258.      case 'O': case 'o':
  259.     mon = 9; break;
  260.      case 'N': case 'n':
  261.     mon = 10; break;
  262.      case 'D': case 'd':
  263.     mon = 11; break;
  264.      default:
  265.     return 0;
  266.     }
  267.  
  268.     year = next_int(&date);
  269.     hour = next_int(&date);
  270.     min = next_int(&date);
  271.     if (*date == ':') next_int(&date);
  272.  
  273.     if (year >= 100) year -= 1900;    /* xxYY -> YY */
  274.     year -= NN_YEAR_ORIGIN - 1900;    /* base is NN_YEAR_ORIGIN */
  275.     if (year < 0) year += 100;
  276.  
  277.     /* Set `min' to be the number of minutes after midnight UTC.  */
  278.     min += hour*60 - tzone(date);
  279.     for (;  min < 0;  min += 24 * 60)
  280.     if (--day <= 0) {
  281.         if (--mon < 0) {
  282.         --year;
  283.         mon = 11;
  284.         }
  285.         day = month_days(year + NN_YEAR_ORIGIN, mon);
  286.     }
  287.     for (;  24 * 60 <= min;  min -= 24 * 60)
  288.     if (month_days(year + NN_YEAR_ORIGIN, mon) < ++day) {
  289.         if (11 < ++mon) {
  290.         ++year;
  291.         mon = 0;
  292.         }
  293.         day = 1;
  294.     }
  295.  
  296.     res = (year * 12 + mon) * 31 + day - 1;
  297.     res *= 24 * 60;
  298.     res += min;
  299.  
  300.     return res;
  301. }
  302.  
  303.  
  304. #ifdef DATE_TEST
  305.  
  306.  
  307. main()
  308. {
  309.     char buffer[128];
  310.     char *dp;
  311.     unsigned long t;
  312.  
  313.     while (fgets(buffer, 128, stdin)) {
  314.     if (strncmp(buffer, "Date:", 5)) continue;
  315.  
  316.     dp = strchr(buffer, ':');
  317.     if (dp == NULL) continue;
  318.     dp++;
  319.     while (isspace(*dp)) dp++;
  320.     t = pack_date(dp);
  321.     printf("%lu\t%s\n", t, dp);
  322.     }
  323.  
  324.     exit(0);
  325. }
  326.  
  327. #endif
  328.