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 >
Wrap
C/C++ Source or Header
|
1992-10-30
|
16KB
|
544 lines
/* @(#)dates.c 3.0 (c) copyright 3/01/90 (Dan Heller, Bart Schaefer) */
#include "mush.h"
/*
* %ld%3c%s gmt_in_secs weekday orig_timezone
* The standard "date format" stored in the msg data structure.
*/
char *day_names[] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
char *month_names[] = { /* imported in pick.c */
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static int mtbl[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
/* Time Zone Stuff */
struct zoneoff {
char *zname;
int hr_off;
int mn_off;
} time_zones[] = {
/* Universal Time */
{ "UT", 0, 0 }, { "GMT", 0, 0 },
/* European Time */
{ "BST", 1, 0 }, /* Brit. Summer */
{ "EET", 2, 0 }, { "EEST", 3, 0 }, /* Eastern */
{ "EET DST", 3, 0 },
{ "MET", 1, 0 }, { "MEST", 2, 0 }, /* Middle */
{ "MET DST", 2, 0 },
{ "WET", 0, 0 }, { "WEST", 1, 0 }, /* Western */
{ "WET DST", 1, 0 },
/* North American Time */
{ "NST", -3,-30 }, /* Newfoundland */
{ "AST", -4, 0 }, { "ADT", -3, 0 }, /* Atlantic */
{ "EST", -5, 0 }, { "EDT", -4, 0 }, /* Eastern */
{ "CST", -6, 0 }, { "CDT", -5, 0 }, /* Central */
{ "MST", -7, 0 }, { "MDT", -6, 0 }, /* Mountain */
{ "PST", -8, 0 }, { "PDT", -7, 0 }, /* Pacific */
{ "YST", -9, 0 }, { "YDT", -8, 0 }, /* Yukon */
{ "HST", -10, 0 }, { "HDT", -9, 0 }, /* Hawaii */
/* Japan and Australia Time */
{"JST", 9, 0 }, /* Japan */
{"AEST", 10, 0 }, {"AESST", 11, 0 }, /* Eastern */
{"ACST", 9, 30 }, {"ACSST", 10, 30 }, /* Central */
{"AWST", 8, 0 }, /* Western */
/* Military Time */
{ "A", 1, 0 }, { "N", -1, 0 },
{ "B", 2, 0 }, { "O", -2, 0 },
{ "C", 3, 0 }, { "P", -3, 0 },
{ "D", 4, 0 }, { "Q", -4, 0 },
{ "E", 5, 0 }, { "R", -5, 0 },
{ "F", 6, 0 }, { "S", -6, 0 },
{ "G", 7, 0 }, { "T", -7, 0 },
{ "H", 8, 0 }, { "U", -8, 0 },
{ "I", 9, 0 }, { "V", -9, 0 },
{ "K", 10, 0 }, { "W", -10, 0 },
{ "L", 11, 0 }, { "X", -11, 0 },
{ "M", 12, 0 }, { "Y", -12, 0 },
{ "Z", 0, 0 },
/* Also legal is +/- followed by hhmm offset from UT */
{ 0, 0, 0 }
};
long
getzoff(zone)
char *zone;
{
struct zoneoff *z;
int hours, mins;
char sign[2];
if (!zone || !*zone)
return 0;
if (sscanf(zone, "%1[-+]%2d%2d", sign, &hours, &mins) == 3)
return (hours * 3600 + mins * 60) * (*sign == '-' ? -1 : 1);
for (z = time_zones; z->zname; z++)
if (lcase_strncmp(zone, z->zname, -1) == 0)
return z->hr_off * 3600 + z->mn_off * 60;
return 0;
}
/*
* Kind of the reverse of localtime() and gmtime() -- converts a struct tm
* to time in seconds since 1970. Valid until 2038.
* If the "zone" argument is present, it modifies the return value.
* The zone should be a string, either +/-hhmm or symbolic (above).
* The "how" argument should be -1 to convert FROM gmt, 1 to convert TO gmt,
* and (as a "side-effect") 0 if the Zone parameter is to be ignored.
*
* Thanks to ktl@wag240.caltech.edu (Kian-Tat Lim) for similar algorithm
* written in perl from which this was derived.
*/
long
time2gmt(tym, zone, how)
struct tm *tym;
char *zone;
int how;
{
long year, julian;
if (tym->tm_year < 100)
year = tym->tm_year + 1900;
if (year < 69)
year += 100;
julian = 365 * (year - 1970) + (int)((year - 1970 + 1) / 4) +
mtbl[tym->tm_mon] + tym->tm_mday - 1;
/* tym->tm_yday might not be valid */
if (tym->tm_mon > 1 && year%4 == 0 && (year%100 != 0 || year%400 == 0))
julian++;
julian *= 86400; /* convert to seconds */
julian += (tym->tm_hour * 60 + tym->tm_min) * 60 + tym->tm_sec;
return julian - getzoff(zone) * how;
}
struct tm *
time_n_zone(zone)
char *zone;
{
struct tm *T;
char *tz;
#if defined(SYSV) || defined(TIMEZONE)
long x;
(void) time(&x);
T = localtime(&x);
#ifndef TIMEZONE
{
extern char *tzname[];
tz = tzname[T->tm_isdst];
}
#endif /* TIMEZONE */
#else /* SYSV || TIMEZONE */
extern char *timezone();
struct timeval mytime;
struct timezone myzone;
(void) gettimeofday(&mytime, &myzone);
T = localtime(&mytime.tv_sec);
tz = timezone(myzone.tz_minuteswest, (T->tm_isdst && myzone.tz_dsttime));
#endif /* !SYSV */
#ifdef TIMEZONE
#ifdef DAYLITETZ
if (T->tm_isdst)
tz = DAYLITETZ;
else
#endif /* DAYLITETZ */
tz = TIMEZONE;
#endif /* TIMEZONE */
(void) strncpy(zone, tz, 7), zone[7] = 0;
return T;
}
/* Time() returns a string according to criteria:
* if "now" is 0, then the current time is gotten and used.
* else, use the time described by now
* opts points to a string of args which is parsed until an unknown
* arg is found and opts will point to that upon return.
* valid args are T (time of day), D (day of week), M (month), Y (year),
* N (number of day in month -- couldn't think of a better letter).
*/
char *
Time(opts, now)
register char *opts;
long now;
{
static char time_buf[30];
struct tm *T;
register char *p = time_buf;
long x;
if (!opts)
return NULL;
if (now)
x = now;
else
(void) time(&x);
T = localtime(&x);
for (;; opts++) {
switch(*opts) {
case 'T':
if (ison(glob_flags, MIL_TIME))
(void) sprintf(p, "%2d:%02d", T->tm_hour, T->tm_min);
else
(void) sprintf(p, "%d:%02d", (T->tm_hour) ?
((T->tm_hour <= 12) ? T->tm_hour : T->tm_hour - 12) :
12, T->tm_min);
when 'D': case 'W': (void) strcpy(p, day_names[T->tm_wday]);
when 'M': (void) strcpy(p, month_names[T->tm_mon]);
when 'y': (void) sprintf(p, "%d", T->tm_year);
when 'Y': (void) sprintf(p, "%d", T->tm_year + 1900);
when 'N': (void) sprintf(p, "%d", T->tm_mday);
otherwise: *--p = 0; return time_buf;
}
p += strlen(p);
*p++ = ' ';
}
}
/* parse date and return a string that looks like
* %ld%3c%s gmt_in_secs weekday orig_timezone
* This function is a bunch of scanfs on known date formats. Don't
* trust the "weekday" name fields because they may not be spelled
* right, or have the correct punctuation. Figure it out once the
* year and month and date have been determined.
*/
char *
parse_date(p)
register char *p;
{
/* When scanf-ing if month isn't a month, it could be a _long_ string.
* this is also the static buffer whose address we return.
*/
static char month[64];
char Wkday[4], Zone[12], dst[4];
char a_or_p;
int Month = 0, Day = 0, Year = 0;
int Hours = -1, Mins = -1, Secs = -1;
struct tm T;
Zone[0] = dst[0] = 0;
skipspaces(0);
/* programmer's note -- there are too many scanfs here for some compilers
* to put them all into one if statement. Use goto's :-( Also reset
* Zone[0] after any sscanf() that could corrupt it on a partial match.
*
* Not yet handling all possible combinations of mailers using two-word
* time zones, e.g. MET DST instead of MEST. Only the specific case
* where this was reported has been handled here.
*/
/* RFC822 formats and minor variations -- order important */
/* day_number month_name year_number time timezone */
if (sscanf(p, "%d %s %d %d:%d:%d %7s %3s",
&Day, month, &Year, &Hours, &Mins, &Secs, Zone, dst) >= 6 && Day)
goto gotit;
Zone[0] = dst[0] = 0;
if (sscanf(p, "%d %s %d %d:%d %7s",
&Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day) {
Secs = 0;
goto gotit;
}
Zone[0] = dst[0] = 0;
/* day_name day_number month_name year_number time timezone */
if (sscanf(p, "%*s %d %s %d %d:%d:%d %7s %3s",
&Day, month, &Year, &Hours, &Mins, &Secs, Zone, dst) >= 6 && Day)
goto gotit;
Zone[0] = dst[0] = 0;
if (sscanf(p, "%*s %d %s %d %d:%d %7s %3s",
&Day, month, &Year, &Hours, &Mins, Zone, dst) >= 5 && Day) {
Secs = 0;
goto gotit;
}
Zone[0] = dst[0] = 0;
/* Ctime format (From_ lines) -- timezone almost never found */
/* day_name month_name day_number time timezone year_number */
if (sscanf(p, "%*s %s %d %d:%d:%d %7s %d",
month, &Day, &Hours, &Mins, &Secs, Zone, &Year) == 7)
goto gotit;
Zone[0] = 0;
/* day_name month_name day_number time year_number */
if (sscanf(p, "%*s %s %d %d:%d:%d %d",
month, &Day, &Hours, &Mins, &Secs, &Year) == 6)
goto gotit;
/* day_name month_name day_number time timezone dst year_number */
if (sscanf(p, "%*s %s %d %d:%d:%d %7s %3s %d",
month, &Day, &Hours, &Mins, &Secs, Zone, dst, &Year) == 8)
goto gotit;
Zone[0] = dst[0] = 0;
/* Other common variants */
/* day_number month_name year_number time-timezone (day) */
/* ^no colon separator */
if (sscanf(p, "%d %s %d %2d%2d%1[-+]%6[0123456789]",
&Day, month, &Year, &Hours, &Mins, &Secs, &Zone[0], &Zone[1]) == 8)
goto gotit;
if (sscanf(p, "%d %s %d %2d%2d-%7s", /* Does this _ever_ hit? */
&Day, month, &Year, &Hours, &Mins, Zone) == 6) {
Secs = 0;
goto gotit;
}
Zone[0] = 0;
/* day_number month_name year_number time timezone */
/* ^no colon separator */
/* (This is the odd one in the RFC822 examples section; */
/* also catches the slop from partial hits above.) */
if (sscanf(p, "%d %s %d %2d%2d %7s",
&Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day) {
Secs = 0;
goto gotit;
}
Zone[0] = 0;
Zone[1] = 0; /* Yes, Zone[1] -- tested below */
/* day_number month_name year_number, time "-" ?? */
if (sscanf(p,"%d %s %d, %d:%d:%d %1[-+]%6[0123456789]",
&Day, month, &Year, &Hours, &Mins, &Secs,
&Zone[0], &Zone[1]) >= 6 && Day)
goto gotit;
/* day_number month_name year_number 12_hour_time a_or_p */
if (sscanf(p, "%d %s %d %d:%d:%d %cm %7s",
&Day, month, &Year, &Hours, &Mins, &Secs, &a_or_p, Zone) >= 7) {
if (a_or_p == 'p')
Hours += 12;
goto gotit;
}
/* day_name month_name day_number year_number time */
if (sscanf(p, "%*s %s %d %d %d:%d:%d %7s",
month, &Day, &Year, &Hours, &Mins, &Secs, Zone) >= 6)
goto gotit;
Zone[0] = 0;
if (sscanf(p, "%*s %s %d %d %d:%d %7s",
month, &Day, &Year, &Hours, &Mins, Zone) >= 5) {
Secs = 0;
goto gotit;
}
Zone[0] = 0;
/* day_name month_name day_number time timezone year_number */
if (sscanf(p, "%*s %s %d %d:%d:%d %7s %d",
month, &Day, &Hours, &Mins, &Secs, Zone, &Year) == 7)
goto gotit;
Zone[0] = 0;
if (sscanf(p, "%*s %s %d %d:%d %7s %d",
month, &Day, &Hours, &Mins, Zone, &Year) == 6) {
Secs = 0;
goto gotit;
}
Zone[0] = 0;
Secs = 0; /* For the next 3 attempts */
/* day_number-month_name-year time */
if (sscanf(p,"%d-%[^-]-%d %d:%d", &Day, month, &Year, &Hours, &Mins) == 5)
goto gotit;
/* day_name, day_number-month_name-year time */
if (sscanf(p,"%*s %d-%[^-]-%d %d:%d",
&Day, month, &Year, &Hours, &Mins) == 5)
goto gotit;
/* year_number-month_number-day_number time */
if (sscanf(p, "%d-%d-%d %d:%d", &Year, &Month, &Day, &Hours, &Mins) == 5)
goto gotit;
/* month_name day_number time year Zone */
/* (ctime, but without the day name) */
if (sscanf(p, "%s %d %d:%d:%d %d %7s",
month, &Day, &Hours, &Mins, &Secs, &Year, Zone) >= 6)
goto gotit;
Zone[0] = 0;
if (ison(glob_flags, WARNING))
print("Unknown date format: %s\n", p);
return NULL;
gotit:
if (!lcase_strncmp(dst, "dst", -1)) {
(void) strcat(Zone, " ");
(void) strcat(Zone, dst);
}
if (Year > 1900)
Year -= 1900;
if (!Month && (Month = month_to_n(month)) == -1) {
print("bad month: %s\n", p);
return NULL;
}
if (Zone[0] == 0) {
/* Use local time zone if none found -- important for date_recv */
(void) time_n_zone(Zone);
}
{
/* Lots of foolishness with casts for Xenix-286 16-bit ints */
long days_ctr; /* 16-bit ints overflowed Sept 12, 1989 */
days_ctr = ((long)Year * 365L) + ((Year + 3) / 4);
days_ctr += mtbl[Month-1] + Day + 6;
if (Month > 2 && (Year % 4 == 0))
days_ctr++;
(void) (sprintf(Wkday, "%.3s", day_names[(int)(days_ctr % 7L)]));
}
T.tm_sec = Secs;
T.tm_min = Mins;
T.tm_hour = Hours;
T.tm_mday = Day;
T.tm_mon = Month - 1;
T.tm_year = Year;
T.tm_wday = T.tm_yday = 0; /* not used in time2gmt() */
T.tm_isdst = 0; /* determined from Zone */
return sprintf(month, "%ld%s%s", time2gmt(&T, Zone, 1), Wkday, Zone);
}
/* pass a string in the standard date format, put into string.
* return values in buffers provided they are not null.
*/
char *
date_to_string(Date, Yr, Mon, Day, Wkday, Tm, Zone, ret_buf)
char *Date, *Yr, *Mon, *Day, *Wkday, *Tm, *Zone, *ret_buf;
{
long gmt;
struct tm *T;
char a_or_p, *p = ret_buf;
Zone[0] = 0;
(void) sscanf(Date, "%ld%3c%s", &gmt, Wkday, Zone);
Wkday[3] = 0;
gmt += getzoff(Zone);
T = gmtime(&gmt);
a_or_p = (T->tm_hour < 12)? 'a': 'p';
(void) sprintf(Yr, "%d", T->tm_year + 1900);
(void) sprintf(Day, "%d", T->tm_mday);
(void) strcpy(Mon, month_names[T->tm_mon]);
p += strlen(sprintf(p, "%s %2.d, ", Mon, T->tm_mday));
if (ison(glob_flags, MIL_TIME))
(void) sprintf(p, "%2d:%02d",T->tm_hour,T->tm_min);
else
(void) sprintf(p, "%2.d:%02d%cm",
(T->tm_hour)? (T->tm_hour <= 12)? T->tm_hour: T->tm_hour-12: 12,
T->tm_min, a_or_p);
(void) strcpy(Tm, p);
return ret_buf;
}
/* pass a string in the internal mush date format.
* return pointer to static buffer holding ctime-format date.
*/
char *
date_to_ctime(Date)
char *Date;
{
static char ret_buf[32];
long gmt;
ret_buf[0] = 0;
(void) sscanf(Date, "%ld", &gmt);
(void) strcpy(ret_buf, ctime(&gmt));
return ret_buf;
}
/*
* Build a date string according to the specification in the RFC for Date:
*/
char *
rfc_date(buf)
char buf[];
{
struct tm *T;
char zone[8];
T = time_n_zone(zone);
#ifndef USA
{
long zoff_hr, zoff_sec = getzoff(zone);
if (zoff_sec < 0) {
zone[0] = '-';
zoff_sec = -zoff_sec;
} else
zone[0] = '+';
zoff_hr = zoff_sec / 3600;
zoff_sec -= zoff_hr * 3600;
(void) sprintf(&zone[1], "%02d%02d", zoff_hr, zoff_sec / 60);
}
#endif /* !USA */
return sprintf(buf, "%s, %d %s %d %02d:%02d:%02d %s",
day_names[T->tm_wday], /* day name */
T->tm_mday, /* day of the month */
month_names[T->tm_mon], /* month name */
T->tm_year + 1900, /* year number */
T->tm_hour, /* hours (24hr) */
T->tm_min, T->tm_sec, /* mins/secs */
zone); /* timezone */
}
#define JAN 1
#define FEB 2
#define MAR 3
#define APR 4
#define MAY 5
#define JUN 6
#define JUL 7
#define AUG 8
#define SEP 9
#define OCT 10
#define NOV 11
#define DEC 12
/* stolen direct from ELM */
month_to_n(name)
register char *name;
{
/** return the month number given the month name... **/
register char ch;
switch (lower(*name)) {
case 'a' : if ((ch = lower(name[1])) == 'p')
return(APR);
else if (ch == 'u')
return(AUG);
else return(-1); /* error! */
case 'd' : return(DEC);
case 'f' : return(FEB);
case 'j' : if ((ch = lower(name[1])) == 'a')
return(JAN);
else if (ch == 'u') {
if ((ch = lower(name[2])) == 'n')
return(JUN);
else if (ch == 'l')
return(JUL);
else return(-1); /* error! */
}
else return(-1); /* error */
case 'm' : if ((ch = lower(name[2])) == 'r')
return(MAR);
else if (ch == 'y')
return(MAY);
else return(-1); /* error! */
case 'n' : return(NOV);
case 'o' : return(OCT);
case 's' : return(SEP);
default : return(-1);
}
}