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