/* * Convert date and time data formats. * * tm_to_time(tm) convert tm struct to seconds since 1/1/70 * time_to_tm(tp) convert seconds since 1/1/70 to tm struct * get_time() return current time in seconds since 1/1/70 * set_tzone() figure out current timezone (config.tzone) * date_to_time(day, month, year, wkday, yday, weeknum) * Returns day/month/year as # of secs since * 1/1/70, the weekday (0..6), the julian date * (0..365), and the week #. */ #include #if defined(MIPS) || defined(NEWSOS4) #include #endif #include #include #include "conf.h" #undef DEBUG /* define this to debug timezone calc */ extern char *getenv(); extern void get_rsrc(); extern struct config config; /* global configuration data */ short monthlen[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; short monthbegin[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; /* * convert a tm structure (d.m.y h:m:s) to a time in seconds since 1/1/70. * The difference to mktime() is that the result modulo 86400 is always * the time, regardless of timezones and daylight saving time. This makes * it much easier to process days with different DST status. * This routine only works correctly for the range 1970..2069. */ time_t tm_to_time(tm) struct tm *tm; /* time/date to convert */ { time_t t; /* return value */ t = monthbegin[tm->tm_mon] /* full months */ + tm->tm_mday-1 /* full days */ + (!(tm->tm_year & 3) && tm->tm_mon > 1); /* leap day this year*/ tm->tm_yday = t; t += 365 * (tm->tm_year - 70) /* full years */ + (tm->tm_year - 69)/4; /* past leap days */ tm->tm_wday = (t + 4) % 7; t = t*86400 + tm->tm_hour*3600 + tm->tm_min*60 + tm->tm_sec; if (tm->tm_mday > monthlen[tm->tm_mon] + (!(tm->tm_year & 3) && tm->tm_mon == 1)) return((time_t)-1); return(t); } /* * convert time in seconds since 1/1/70 to a tm structure, and also fill in * the week number, julian date, and weekday. The difference to localtime() * is that timezones and daylight saving time are ignored. * This routine only works correctly for the range 1970..2069. */ struct tm *time_to_tm(t) time_t t; /* time to convert */ { int n, l; /* temp */ static struct tm tm; /* returned tm */ tm.tm_sec = t % 60; t /= 60; tm.tm_min = t % 60; t /= 60; tm.tm_hour = t % 24; t /= 24; tm.tm_wday = (t + 4) % 7; n = t / (365+365+366+365); tm.tm_year = 70 + n*4; t -= n * (365+365+366+365); if (t > 364) tm.tm_year++, t -= 365; if (t > 364) tm.tm_year++, t -= 365; if (t > 365) tm.tm_year++, t -= 366; tm.tm_yday = t; for (n=0; n < 11; n++) { l = monthlen[n] + (n==1 && !(tm.tm_year&3)); if (t < l) break; t -= l; } tm.tm_mon = n; tm.tm_mday = t+1; return(&tm); } /* * return current time in seconds since 1/1/70. The timezone is applied * such that the return value modulo 86400 is the number of seconds since * midnight. */ time_t get_time() { return(time(0) - config.tzone + config.adjust_time); } /* * return the current time zone as the number of seconds as difference * to GMT (UTC) in seconds. Since all the tricks with tm_isdst, gmtoff, * _timezone etc turned out to be nonportable, I am now parsing the TZ * environment variable directly. There is nothing standard about standard * libraries when it comes to time calculation! */ void set_tzone() { register struct config *c = &config; time_t now; /* current local time */ time_t tod; /* time-of-day, 0..86399 */ struct tm *tm; /* julian date of */ now = time(0) - c->raw_tzone; tod = now % 86400; tm = time_to_tm(now); switch(c->dst_flag) { case 0: /* dst on */ c->tzone = c->raw_tzone - 3600; break; case 1: /* dst off */ c->tzone = c->raw_tzone; break; case 2: /* auto dst */ if ((tm->tm_yday > c->dst_begin || tm->tm_yday == c->dst_begin && tod >= c->dst_begin_time)&& (tm->tm_yday == c->dst_end && tod < c->dst_end_time || tm->tm_yday < c->dst_end)) c->tzone = c->raw_tzone - 3600; else c->tzone = c->raw_tzone; } } /* * Figure out reasonable values for the config fields raw_tzone, dst_flag, * dst_begin, and dst_end. This is used for defaults if the .dayplan file * does not specify the adjustment, or when the user presses Guess in the * Adjust Time popup. */ #define DST_BEGIN 64 /* default begin of DST */ #define DST_END 220 /* default end of DST */ #ifdef DEBUG static char *zone_time_string(); #endif static get_zone_time(); static int julian_year_date(); guess_tzone() { char *tz=0; /* timezone string (TZ) */ int zone=0, dst= -3600; /* difference from GMT */ int begin=0, begintime=0; /* dst begin: julian day/time*/ int end=0, endtime=0; /* dst end: julian day/time */ if (((tz = getenv("PLAN_TZ")) && *tz || (tz = getenv("TZ")) && *tz) && strrchr("0123456789-",tz[3])){ do { if (!*tz++ || !*tz++ || !*tz++) /* MET */ break; zone = get_zone_time(&tz, 0); /* time */ dst = zone - 3600; if (!*tz++ || !*tz++ || !*tz++) /* MST */ break; if (*tz>='A' && *tz<='Z') tz++; /* allow MESZ */ dst = get_zone_time(&tz, dst); /* time */ if (*tz == ';') { /* ;begin */ tz++; while (*tz >= '0' && *tz <= '9') begin = begin*10 + *tz++ - '0'; if (*tz == '/') { /* /time */ tz++; begintime = get_zone_time(&tz, 0); } if (*tz++ != ',') /* ,end */ break; while (*tz >= '0' && *tz <= '9') end = end*10 + *tz++ - '0'; if (*tz++ == '/') /* /time */ endtime = get_zone_time(&tz, 0); } else if (*tz == ',') { /* ,begin */ tz++; if (*tz != 'M') break; begin = julian_year_date(&tz); if (*tz == '/') { tz++; /* /time */ begintime = get_zone_time(&tz, 0); } else begintime= 2*3600; /* at 2 AM */ if (*tz == ',') { /* ,end */ tz++; if (*tz != 'M') break; end = julian_year_date(&tz); if (*tz == '/') { /* /time */ tz++; endtime = get_zone_time(&tz,0); } else endtime = 2*3600;/* at 2 AM */ } } else break; } while (0); } else { #if defined(SVR4) || defined(SOLARIS2) time_t now = get_time(); struct tm *tm = localtime(&now); time_t h = tm->tm_hour; struct tm *gmt = gmtime(&now); zone = (gmt->tm_hour - h) * 3600; dst = zone - 3600; dst = 0; /* jmorriso@bogomips.ee.ubc.ca 28.8.94 */ #else #if defined(bsdi) || defined(SUN) || defined(SVR4) || defined(convex) time_t now; struct tm *tm; tzset(); now = time(0); tm = localtime(&now); zone = -tm->tm_gmtoff; #endif #endif } #ifdef DEBUG if (tz = getenv("PLAN_TZ")) printf("PLAN_TZ env variable: %s\n", tz); if (tz = getenv("TZ")) printf("TZ env variable: %s\n", tz); printf("standard timezone: GMT%s\n", zone_time_string(zone)); printf("DST timezone: GMT%s\n", zone_time_string(dst)); printf("DST start: %s on julian day %d\n", zone_time_string(begintime)+1, begin); printf("DST end: %s on julian day %d\n", zone_time_string(endtime)+1, end); #endif if (!begin) { /* defaults */ begin = DST_BEGIN; begintime = 2*3600; } if (!end) { end = DST_END; endtime = 2*3600; } config.raw_tzone = zone; config.dst_flag = 2; config.dst_begin = begin; config.dst_end = end; config.dst_begin_time = begintime; config.dst_end_time = endtime; #ifdef DEBUG printf("current timezone: GMT%s\n", zone_time_string(zone)); #endif } /* * parse a time string of the form [hours[:minutes[:seconds]]], return seconds */ static get_zone_time(tzp, zdefault) char **tzp; int zdefault; { char *tz = *tzp; int i, num, zone = 0; int sign = 1; if (*tz == '-') tz++, sign = -1; if (*tz == '+') tz++; for (i=0; i < 3; i++) { zone *= 60; num = 0; while (*tz >= '0' && *tz <= '9') num = num*10 + *tz++ - '0'; zone += num; tz += *tz == ':'; } zone *= sign; if (tz == *tzp) zone = zdefault; *tzp = tz; return(zone); } /* * parse a date in format 'Mmm.ww.dd', where * ww is a week number, mm a month number, and dd a day code starting from * Sunday (0); week 5 denotes always the last week of a month. * (leading zeros may be omitted in all cases) * * The dates may optionally be followed by a time designation after a slash. * The default for the time is 2 AM. * * interface: the scanning pointer tzp must be incremented * * This routine by Klaus Guntermann */ extern int curr_year; /* year being displayed, since 1900 */ time_t date_to_time(); static int julian_year_date(tzp) char **tzp; { char *tz = *tzp; int day, month, week; int wkday, julian; if (*tz == 'M') { /* scan the format */ tz++; month = 0; while (*tz >= '0' && *tz <= '9') month = month*10 + *tz++ - '0'; if (*tz != '.') { *tzp = tz; return(0); } tz++; week = 0; while (*tz >= '0' && *tz <= '9') week = week*10 + *tz++ - '0'; if (*tz != '.') { *tzp = tz; return(0); } tz++; day=0; while (*tz >= '0' && *tz <= '9') day = day*10 + *tz++ - '0'; /* compute the corresponding Julian date in this year */ if (week<5) { /* did not ask for a day in the last week of a month */ (void)date_to_time(1, month-1, curr_year, &wkday, &julian, NULL); julian += 1+day-wkday; /* advance to proper day */ julian += (week - (wkday <= day ? 1 : 0)) * 7; /* and correct for the number of weeks wanted */ } else { /* compute backwards from the 1st day of the next month, which may be the first month of the next year */ (void)date_to_time(1, (month < 11 ? month : 0), (month<11 ? curr_year : curr_year+1), &wkday, &julian, NULL); /* now go back to the proper day... */ julian += 1 + day - wkday - (day >= wkday ? 7 : 0); } } *tzp = tz; return(julian); } /* * return a number of seconds as a string "+/-hours:minutes:seconds" */ #ifdef DEBUG static char *zone_time_string(t) int t; { static char buf[12]; t %= 86400; buf[0] = t < 0 ? '-' : '+'; if (t < 0) t = -t; sprintf(buf+1, "%02d:%02d:%02d", t/3600, (t/60)%60, t%60); return(buf); } #endif /* * day/month/year to time. Return 0 if something is wrong. * Also return the weekday, julian date, and week number of that date. * Note that *wkday counts from 0=sunday to 6=saturday. */ time_t date_to_time(day, month, year, wkday, julian, weeknum) int day, month, year, *wkday, *julian, *weeknum; { struct tm tm; time_t time; tm.tm_sec = 0; tm.tm_min = 0; tm.tm_hour = 0; tm.tm_mday = day; tm.tm_mon = month; tm.tm_year = year; time = tm_to_time(&tm); if (wkday) *wkday = tm.tm_wday; if (julian) *julian = tm.tm_yday; if (weeknum) *weeknum = config.fullweek ? tm.tm_yday / 7 : tm.tm_yday ? ((tm.tm_yday - 1) /7) + 1 : 0; return(time == -1 || day != tm.tm_mday ? 0 : time); }