home *** CD-ROM | disk | FTP | other *** search
/ ftp.freefriends.org / ftp.freefriends.org.tar / ftp.freefriends.org / arnold / Source / mush.rstevens.tar.gz / mush.tar / dates.c < prev    next >
C/C++ Source or Header  |  1992-10-30  |  16KB  |  544 lines

  1. /* @(#)dates.c    3.0    (c) copyright 3/01/90 (Dan Heller, Bart Schaefer) */
  2.  
  3. #include "mush.h"
  4.  
  5. /*
  6.  *   %ld%3c%s    gmt_in_secs weekday orig_timezone
  7.  * The standard "date format" stored in the msg data structure.
  8.  */
  9. char *day_names[] = {
  10.     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  11. };
  12. char *month_names[] = {     /* imported in pick.c */
  13.     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  14.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  15. };
  16.  
  17. static int mtbl[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
  18.  
  19. /* Time Zone Stuff */
  20. struct zoneoff {
  21.     char *zname;
  22.     int hr_off;
  23.     int mn_off;
  24. } time_zones[] = {
  25.     /* Universal Time */
  26.     { "UT",      0,  0 },    { "GMT",      0,  0 },
  27.     /* European Time */
  28.     { "BST",      1,  0 },                    /* Brit. Summer */
  29.     { "EET",      2,  0 },    { "EEST",      3,  0 },    /* Eastern */
  30.                     { "EET DST",      3,  0 },
  31.     { "MET",      1,  0 },    { "MEST",      2,  0 },    /* Middle */
  32.                     { "MET DST",      2,  0 },
  33.     { "WET",      0,  0 },    { "WEST",      1,  0 },    /* Western */
  34.                     { "WET DST",      1,  0 },
  35.     /* North American Time */
  36.     { "NST",     -3,-30 },                    /* Newfoundland */
  37.     { "AST",     -4,  0 },    { "ADT",     -3,  0 },    /* Atlantic */
  38.     { "EST",     -5,  0 },    { "EDT",     -4,  0 },    /* Eastern */
  39.     { "CST",     -6,  0 },    { "CDT",     -5,  0 },    /* Central */
  40.     { "MST",     -7,  0 },    { "MDT",     -6,  0 },    /* Mountain */
  41.     { "PST",     -8,  0 },    { "PDT",     -7,  0 },    /* Pacific */
  42.     { "YST",     -9,  0 },    { "YDT",     -8,  0 },    /* Yukon */
  43.     { "HST",    -10,  0 },    { "HDT",     -9,  0 },    /* Hawaii */
  44.     /* Japan and Australia Time */
  45.     {"JST",      9,  0 },                    /* Japan */
  46.     {"AEST",     10,  0 },    {"AESST",     11,  0 },    /* Eastern */    
  47.     {"ACST",      9, 30 },    {"ACSST",     10, 30 },    /* Central */
  48.     {"AWST",      8,  0 },                    /* Western */
  49.     /* Military Time */
  50.     { "A",      1,  0 },    { "N",         -1,  0 },
  51.     { "B",      2,  0 },    { "O",         -2,  0 },
  52.     { "C",      3,  0 },    { "P",         -3,  0 },
  53.     { "D",      4,  0 },    { "Q",         -4,  0 },
  54.     { "E",      5,  0 },    { "R",         -5,  0 },
  55.     { "F",      6,  0 },    { "S",         -6,  0 },
  56.     { "G",      7,  0 },    { "T",         -7,  0 },
  57.     { "H",      8,  0 },    { "U",         -8,  0 },
  58.     { "I",      9,  0 },    { "V",         -9,  0 },
  59.     { "K",     10,  0 },    { "W",        -10,  0 },
  60.     { "L",     11,  0 },    { "X",        -11,  0 },
  61.     { "M",     12,  0 },    { "Y",        -12,  0 },
  62.     { "Z",      0,  0 },
  63.     /* Also legal is +/- followed by hhmm offset from UT */
  64.     { 0, 0, 0 }
  65. };
  66.  
  67. long
  68. getzoff(zone)
  69. char *zone;
  70. {
  71.     struct zoneoff *z;
  72.     int hours, mins;
  73.     char sign[2];
  74.  
  75.     if (!zone || !*zone)
  76.     return 0;
  77.     if (sscanf(zone, "%1[-+]%2d%2d", sign, &hours, &mins) == 3)
  78.     return (hours * 3600 + mins * 60) * (*sign == '-' ? -1 : 1);
  79.     for (z = time_zones; z->zname; z++)
  80.     if (lcase_strncmp(zone, z->zname, -1) == 0)
  81.         return z->hr_off * 3600 + z->mn_off * 60;
  82.     return 0;
  83. }
  84.  
  85. /*
  86.  * Kind of the reverse of localtime() and gmtime() -- converts a struct tm
  87.  * to time in seconds since 1970.  Valid until 2038.
  88.  * If the "zone" argument is present, it modifies the return value.
  89.  * The zone should be a string, either +/-hhmm or symbolic (above).
  90.  * The "how" argument should be -1 to convert FROM gmt, 1 to convert TO gmt,
  91.  * and (as a "side-effect") 0 if the Zone parameter is to be ignored.
  92.  *
  93.  * Thanks to ktl@wag240.caltech.edu (Kian-Tat Lim) for similar algorithm
  94.  * written in perl from which this was derived.
  95.  */
  96. long
  97. time2gmt(tym, zone, how)
  98. struct tm *tym;
  99. char *zone;
  100. int how;
  101. {
  102.     long year, julian;
  103.  
  104.     if (tym->tm_year < 100)
  105.     year = tym->tm_year + 1900;
  106.     if (year < 69)
  107.     year += 100;
  108.  
  109.     julian = 365 * (year - 1970) + (int)((year - 1970 + 1) / 4) +
  110.         mtbl[tym->tm_mon] + tym->tm_mday - 1;
  111.         /* tym->tm_yday might not be valid */
  112.     if (tym->tm_mon > 1 && year%4 == 0 && (year%100 != 0 || year%400 == 0))
  113.     julian++;
  114.     julian *= 86400;    /* convert to seconds */
  115.     julian += (tym->tm_hour * 60 + tym->tm_min) * 60 + tym->tm_sec;
  116.     return julian - getzoff(zone) * how;
  117. }
  118.  
  119. struct tm *
  120. time_n_zone(zone)
  121. char *zone;
  122. {
  123.     struct tm *T;
  124.     char *tz;
  125. #if defined(SYSV) || defined(TIMEZONE)
  126.     long      x;
  127.  
  128.     (void) time(&x);
  129.     T = localtime(&x);
  130. #ifndef TIMEZONE
  131.     {
  132.     extern char *tzname[];
  133.     tz = tzname[T->tm_isdst];
  134.     }
  135. #endif /* TIMEZONE */
  136. #else /* SYSV || TIMEZONE */
  137.     extern char     *timezone();
  138.     struct timeval  mytime;
  139.     struct timezone myzone;
  140.  
  141.     (void) gettimeofday(&mytime, &myzone);
  142.     T = localtime(&mytime.tv_sec);
  143.     tz = timezone(myzone.tz_minuteswest, (T->tm_isdst && myzone.tz_dsttime));
  144. #endif /* !SYSV */
  145.  
  146. #ifdef TIMEZONE
  147. #ifdef DAYLITETZ
  148.     if (T->tm_isdst)
  149.     tz = DAYLITETZ;
  150.     else
  151. #endif /* DAYLITETZ */
  152.     tz = TIMEZONE;
  153. #endif /* TIMEZONE */
  154.  
  155.     (void) strncpy(zone, tz, 7), zone[7] = 0;
  156.     return T;
  157. }
  158.  
  159. /* Time() returns a string according to criteria:
  160.  *   if "now" is 0, then the current time is gotten and used.
  161.  *       else, use the time described by now
  162.  *   opts points to a string of args which is parsed until an unknown
  163.  *       arg is found and opts will point to that upon return.
  164.  *   valid args are T (time of day), D (day of week), M (month), Y (year),
  165.  *       N (number of day in month -- couldn't think of a better letter).
  166.  */
  167. char *
  168. Time(opts, now)
  169. register char *opts;
  170. long now;
  171. {
  172.     static char time_buf[30];
  173.     struct tm       *T;
  174.     register char *p = time_buf;
  175.     long      x;
  176.  
  177.     if (!opts)
  178.     return NULL;
  179.     if (now)
  180.     x = now;
  181.     else
  182.     (void) time(&x);
  183.     T = localtime(&x);
  184.     for (;; opts++) {
  185.     switch(*opts) {
  186.         case 'T':
  187.         if (ison(glob_flags, MIL_TIME))
  188.             (void) sprintf(p, "%2d:%02d", T->tm_hour, T->tm_min);
  189.         else
  190.             (void) sprintf(p, "%d:%02d", (T->tm_hour) ?
  191.               ((T->tm_hour <= 12) ? T->tm_hour : T->tm_hour - 12) :
  192.               12, T->tm_min);
  193.         when 'D': case 'W': (void) strcpy(p, day_names[T->tm_wday]);
  194.         when 'M': (void) strcpy(p, month_names[T->tm_mon]);
  195.         when 'y': (void) sprintf(p, "%d", T->tm_year);
  196.         when 'Y': (void) sprintf(p, "%d", T->tm_year + 1900);
  197.         when 'N': (void) sprintf(p, "%d", T->tm_mday);
  198.         otherwise: *--p = 0; return time_buf;
  199.     }
  200.     p += strlen(p);
  201.     *p++ = ' ';
  202.     }
  203. }
  204.  
  205. /* parse date and return a string that looks like
  206.  *   %ld%3c%s    gmt_in_secs weekday orig_timezone
  207.  * This function is a bunch of scanfs on known date formats.  Don't
  208.  * trust the "weekday" name fields because they may not be spelled
  209.  * right, or have the correct punctuation.  Figure it out once the
  210.  * year and month and date have been determined.
  211.  */
  212. char *
  213. parse_date(p)
  214. register char *p;
  215. {
  216.     /* When scanf-ing if month isn't a month, it could be a _long_ string.
  217.      * this is also the static buffer whose address we return.
  218.      */
  219.     static char month[64];
  220.     char Wkday[4], Zone[12], dst[4];
  221.     char a_or_p;
  222.     int Month = 0, Day = 0, Year = 0;
  223.     int Hours = -1, Mins = -1, Secs = -1;
  224.     struct tm T;
  225.  
  226.     Zone[0] = dst[0] = 0;
  227.     skipspaces(0);
  228.  
  229.     /* programmer's note -- there are too many scanfs here for some compilers
  230.      * to put them all into one if statement.  Use goto's :-(  Also reset
  231.      * Zone[0] after any sscanf() that could corrupt it on a partial match.
  232.      *
  233.      * Not yet handling all possible combinations of mailers using two-word
  234.      * time zones, e.g. MET DST instead of MEST.  Only the specific case
  235.      * where this was reported has been handled here.
  236.      */
  237.  
  238.     /* RFC822 formats and minor variations -- order important */
  239.  
  240.     /*   day_number month_name year_number time timezone */
  241.     if (sscanf(p, "%d %s %d %d:%d:%d %7s %3s",
  242.         &Day, month, &Year, &Hours, &Mins, &Secs, Zone, dst) >= 6 && Day)
  243.     goto gotit;
  244.     Zone[0] = dst[0] = 0;
  245.     if (sscanf(p, "%d %s %d %d:%d %7s",
  246.         &Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day) {
  247.     Secs = 0;
  248.     goto gotit;
  249.     }
  250.     Zone[0] = dst[0] = 0;
  251.     /*   day_name day_number month_name year_number time timezone */
  252.     if (sscanf(p, "%*s %d %s %d %d:%d:%d %7s %3s",
  253.         &Day, month, &Year, &Hours, &Mins, &Secs, Zone, dst) >= 6 && Day)
  254.     goto gotit;
  255.     Zone[0] = dst[0] = 0;
  256.     if (sscanf(p, "%*s %d %s %d %d:%d %7s %3s",
  257.         &Day, month, &Year, &Hours, &Mins, Zone, dst) >= 5 && Day) {
  258.     Secs = 0;
  259.     goto gotit;
  260.     }
  261.     Zone[0] = dst[0] = 0;
  262.  
  263.     /* Ctime format (From_ lines) -- timezone almost never found */
  264.  
  265.     /*   day_name month_name day_number time timezone year_number */
  266.     if (sscanf(p, "%*s %s %d %d:%d:%d %7s %d",
  267.         month, &Day, &Hours, &Mins, &Secs, Zone, &Year) == 7)
  268.     goto gotit;
  269.     Zone[0] = 0;
  270.     /*   day_name month_name day_number time year_number */
  271.     if (sscanf(p, "%*s %s %d %d:%d:%d %d",
  272.         month, &Day, &Hours, &Mins, &Secs, &Year) == 6)
  273.     goto gotit;
  274.     /*   day_name month_name day_number time timezone dst year_number */
  275.     if (sscanf(p, "%*s %s %d %d:%d:%d %7s %3s %d",
  276.         month, &Day, &Hours, &Mins, &Secs, Zone, dst, &Year) == 8)
  277.     goto gotit;
  278.     Zone[0] = dst[0] = 0;
  279.  
  280.     /* Other common variants */
  281.  
  282.     /*   day_number month_name year_number time-timezone (day) */
  283.     /*                                       ^no colon separator */
  284.     if (sscanf(p, "%d %s %d %2d%2d%1[-+]%6[0123456789]",
  285.         &Day, month, &Year, &Hours, &Mins, &Secs, &Zone[0], &Zone[1]) == 8)
  286.     goto gotit;
  287.     if (sscanf(p, "%d %s %d %2d%2d-%7s",    /* Does this _ever_ hit? */
  288.         &Day, month, &Year, &Hours, &Mins, Zone) == 6) {
  289.     Secs = 0;
  290.     goto gotit;
  291.     }
  292.     Zone[0] = 0;
  293.  
  294.     /*   day_number month_name year_number time timezone    */
  295.     /*                                      ^no colon separator */
  296.     /*   (This is the odd one in the RFC822 examples section;    */
  297.     /*    also catches the slop from partial hits above.)    */
  298.     if (sscanf(p, "%d %s %d %2d%2d %7s",
  299.         &Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day) {
  300.     Secs = 0;
  301.     goto gotit;
  302.     }
  303.     Zone[0] = 0;
  304.     
  305.     Zone[1] = 0;    /* Yes, Zone[1] -- tested below */
  306.  
  307.     /*   day_number month_name year_number, time "-" ?? */
  308.     if (sscanf(p,"%d %s %d, %d:%d:%d %1[-+]%6[0123456789]",
  309.         &Day, month, &Year, &Hours, &Mins, &Secs,
  310.         &Zone[0], &Zone[1]) >= 6 && Day)
  311.     goto gotit;
  312.  
  313.     /*   day_number month_name year_number 12_hour_time a_or_p */
  314.     if (sscanf(p, "%d %s %d %d:%d:%d %cm %7s",
  315.         &Day, month, &Year, &Hours, &Mins, &Secs, &a_or_p, Zone) >= 7) {
  316.     if (a_or_p == 'p')
  317.         Hours += 12;
  318.     goto gotit;
  319.     }
  320.  
  321.     /*   day_name month_name day_number year_number time */
  322.     if (sscanf(p, "%*s %s %d %d %d:%d:%d %7s",
  323.         month, &Day, &Year, &Hours, &Mins, &Secs, Zone) >= 6)
  324.     goto gotit;
  325.     Zone[0] = 0;
  326.     if (sscanf(p, "%*s %s %d %d %d:%d %7s",
  327.         month, &Day, &Year, &Hours, &Mins, Zone) >= 5) {
  328.     Secs = 0;
  329.     goto gotit;
  330.     }
  331.     Zone[0] = 0;
  332.  
  333.     /*   day_name month_name day_number time timezone year_number */
  334.     if (sscanf(p, "%*s %s %d %d:%d:%d %7s %d",
  335.         month, &Day, &Hours, &Mins, &Secs, Zone, &Year) == 7)
  336.     goto gotit;
  337.     Zone[0] = 0;
  338.     if (sscanf(p, "%*s %s %d %d:%d %7s %d",
  339.         month, &Day, &Hours, &Mins, Zone, &Year) == 6) {
  340.     Secs = 0;
  341.     goto gotit;
  342.     }
  343.     Zone[0] = 0;
  344.  
  345.     Secs = 0;    /* For the next 3 attempts */
  346.  
  347.     /*   day_number-month_name-year time */
  348.     if (sscanf(p,"%d-%[^-]-%d %d:%d", &Day, month, &Year, &Hours, &Mins) == 5)
  349.     goto gotit;
  350.  
  351.     /*   day_name, day_number-month_name-year time */
  352.     if (sscanf(p,"%*s %d-%[^-]-%d %d:%d",
  353.         &Day, month, &Year, &Hours, &Mins) == 5)
  354.     goto gotit;
  355.  
  356.     /*   year_number-month_number-day_number time */
  357.     if (sscanf(p, "%d-%d-%d %d:%d", &Year, &Month, &Day, &Hours, &Mins) == 5)
  358.     goto gotit;
  359.  
  360.     /*   month_name day_number time year Zone */
  361.     /*   (ctime, but without the day name)    */
  362.     if (sscanf(p, "%s %d %d:%d:%d %d %7s",
  363.         month, &Day, &Hours, &Mins, &Secs, &Year, Zone) >= 6)
  364.     goto gotit;
  365.     Zone[0] = 0;
  366.  
  367.     if (ison(glob_flags, WARNING))
  368.     print("Unknown date format: %s\n", p);
  369.     return NULL;
  370.  
  371. gotit:
  372.     if (!lcase_strncmp(dst, "dst", -1)) {
  373.     (void) strcat(Zone, " ");
  374.     (void) strcat(Zone, dst);
  375.     }
  376.     if (Year > 1900)
  377.     Year -= 1900;
  378.     if (!Month && (Month = month_to_n(month)) == -1) {
  379.     print("bad month: %s\n", p);
  380.     return NULL;
  381.     }
  382.     if (Zone[0] == 0) {
  383.     /* Use local time zone if none found -- important for date_recv */
  384.     (void) time_n_zone(Zone);
  385.     }
  386.     {
  387.     /* Lots of foolishness with casts for Xenix-286 16-bit ints */
  388.  
  389.     long days_ctr;    /* 16-bit ints overflowed Sept 12, 1989 */
  390.  
  391.         days_ctr = ((long)Year * 365L) + ((Year + 3) / 4);
  392.         days_ctr += mtbl[Month-1] + Day + 6;
  393.         if (Month > 2 && (Year % 4 == 0))
  394.         days_ctr++;
  395.         (void) (sprintf(Wkday, "%.3s", day_names[(int)(days_ctr % 7L)]));
  396.     }
  397.     T.tm_sec = Secs;
  398.     T.tm_min = Mins;
  399.     T.tm_hour = Hours;
  400.     T.tm_mday = Day;
  401.     T.tm_mon = Month - 1;
  402.     T.tm_year = Year;
  403.     T.tm_wday = T.tm_yday = 0;    /* not used in time2gmt() */
  404.     T.tm_isdst = 0;        /* determined from Zone */
  405.     return sprintf(month, "%ld%s%s", time2gmt(&T, Zone, 1), Wkday, Zone);
  406. }
  407.  
  408. /* pass a string in the standard date format, put into string.
  409.  * return values in buffers provided they are not null.
  410.  */
  411. char *
  412. date_to_string(Date, Yr, Mon, Day, Wkday, Tm, Zone, ret_buf)
  413. char *Date, *Yr, *Mon, *Day, *Wkday, *Tm, *Zone, *ret_buf;
  414. {
  415.     long gmt;
  416.     struct tm *T;
  417.     char a_or_p, *p = ret_buf;
  418.  
  419.     Zone[0] = 0;
  420.     (void) sscanf(Date, "%ld%3c%s", &gmt, Wkday, Zone);
  421.     Wkday[3] = 0;
  422.     gmt += getzoff(Zone);
  423.     T = gmtime(&gmt);
  424.     a_or_p = (T->tm_hour < 12)? 'a': 'p';
  425.  
  426.     (void) sprintf(Yr, "%d", T->tm_year + 1900);
  427.     (void) sprintf(Day, "%d", T->tm_mday);
  428.     (void) strcpy(Mon, month_names[T->tm_mon]);
  429.     p += strlen(sprintf(p, "%s %2.d, ", Mon, T->tm_mday));
  430.  
  431.     if (ison(glob_flags, MIL_TIME))
  432.     (void) sprintf(p, "%2d:%02d",T->tm_hour,T->tm_min);
  433.     else
  434.     (void) sprintf(p, "%2.d:%02d%cm",
  435.           (T->tm_hour)? (T->tm_hour <= 12)? T->tm_hour: T->tm_hour-12: 12,
  436.           T->tm_min, a_or_p);
  437.     (void) strcpy(Tm, p);
  438.  
  439.     return ret_buf;
  440. }
  441.  
  442. /* pass a string in the internal mush date format.
  443.  * return pointer to static buffer holding ctime-format date.
  444.  */
  445. char *
  446. date_to_ctime(Date)
  447. char *Date;
  448. {
  449.     static char ret_buf[32];
  450.     long gmt;
  451.  
  452.     ret_buf[0] = 0;
  453.     (void) sscanf(Date, "%ld", &gmt);
  454.     (void) strcpy(ret_buf, ctime(&gmt));
  455.  
  456.     return ret_buf;
  457. }
  458.  
  459. /*
  460.  * Build a date string according to the specification in the RFC for Date:
  461.  */
  462. char *
  463. rfc_date(buf)
  464. char buf[];
  465. {
  466.     struct tm *T;
  467.     char zone[8];
  468.  
  469.     T = time_n_zone(zone);
  470. #ifndef USA
  471.     {
  472.     long zoff_hr, zoff_sec = getzoff(zone);
  473.     if (zoff_sec < 0) {
  474.         zone[0] = '-';
  475.         zoff_sec = -zoff_sec; 
  476.     } else
  477.         zone[0] = '+';
  478.     zoff_hr = zoff_sec / 3600;
  479.     zoff_sec -= zoff_hr * 3600;
  480.     (void) sprintf(&zone[1], "%02d%02d", zoff_hr, zoff_sec / 60);
  481.     }
  482. #endif /* !USA */
  483.  
  484.     return sprintf(buf, "%s, %d %s %d %02d:%02d:%02d %s",
  485.     day_names[T->tm_wday],    /* day name */
  486.     T->tm_mday,        /* day of the month */
  487.     month_names[T->tm_mon],    /* month name */
  488.     T->tm_year + 1900,    /* year number */
  489.     T->tm_hour,        /* hours (24hr) */
  490.     T->tm_min, T->tm_sec,    /* mins/secs */
  491.     zone);            /* timezone */
  492. }
  493.  
  494. #define JAN    1
  495. #define FEB    2
  496. #define MAR    3
  497. #define APR    4
  498. #define MAY    5
  499. #define JUN    6
  500. #define JUL    7
  501. #define AUG    8
  502. #define SEP    9
  503. #define OCT    10
  504. #define NOV    11
  505. #define DEC    12
  506.  
  507. /* stolen direct from ELM */
  508. month_to_n(name)
  509. register char *name;
  510. {
  511.     /** return the month number given the month name... **/
  512.  
  513.     register char ch;
  514.  
  515.     switch (lower(*name)) {
  516.     case 'a' : if ((ch = lower(name[1])) == 'p')
  517.                return(APR);
  518.            else if (ch == 'u')
  519.                return(AUG);
  520.            else return(-1);    /* error! */
  521.     case 'd' : return(DEC);
  522.     case 'f' : return(FEB);
  523.     case 'j' : if ((ch = lower(name[1])) == 'a')
  524.                return(JAN);
  525.            else if (ch == 'u') {
  526.              if ((ch = lower(name[2])) == 'n')
  527.              return(JUN);
  528.              else if (ch == 'l')
  529.              return(JUL);
  530.              else return(-1);        /* error! */
  531.            }
  532.            else return(-1);        /* error */
  533.     case 'm' : if ((ch = lower(name[2])) == 'r')
  534.                return(MAR);
  535.            else if (ch == 'y')
  536.                return(MAY);
  537.            else return(-1);        /* error! */
  538.     case 'n' : return(NOV);
  539.     case 'o' : return(OCT);
  540.     case 's' : return(SEP);
  541.     default  : return(-1);
  542.     }
  543. }
  544.