*/
/* $Header$ */
+#include <ctype.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
-#ifdef __BSD4_2
-#include <sys/time.h>
-#elif !defined(__USG)
-struct timeb
-{
- timez_t time;
- unsigned short millitm;
- short timezone;
- short dstflag;
+#if defined(__BSD4_2)
+
+struct timeval {
+ long tv_sec; /* seconds */
+ long tv_usec; /* and microseconds */
+};
+
+struct timezone {
+ int tz_minuteswest; /* minutes west of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+
+int gettimeofday(struct timeval *tp, struct timezone *tzp);
+
+#elif !defined(_POSIX_SOURCE) && !defined(__USG)
+#if !defined(_MINIX) /* MINIX has no ftime() */
+struct timeb {
+ long time;
+ undigned short millitm;
+ short timezone;
+ short dstflag;
};
+void ftime(struct timeb *bp);
+#endif
#endif
#include "loc_incl.h"
-#ifdef __USG
-long timezone = -1 * 60;
+/* The following define of TZ_LEN must match the number of characters
+ * of the elements of tzname.
+ */
+#define TZ_LEN 10
+#define RULE_LEN 120
+
+#if defined(__USG) || defined(_POSIX_SOURCE)
+char *tzname[2] = {"MET\0\0\0\0\0\0\0", "MDT\0\0\0\0\0\0\0"};
+
+#if defined(__USG)
+long timezone = -1 * 60 * 60;
int daylight = 1;
-char *tzname[] = {"MET", "MDT",};
#endif
+#endif
+
+long _timezone = -1 * 60 * 60;
+long _dst_off = 60 * 60;
+int _daylight = -1;
+char *_tzname[2] = {"MET\0\0\0\0\0\0\0", "MDT\0\0\0\0\0\0\0"};
-long __timezone = -1 * 60;
-static int __daylight = 1;
-char *__tzname[] = {"MET", "MDT",};
+static struct dsttype {
+ char ds_type; /* Unknown, Julian, Zero-based or M */
+ int ds_date[3]; /* months, weeks, days */
+ long ds_sec; /* usually 02:00:00 */
+} dststart = { 'U', { 0, 0, 0 }, 2 * 60 * 60 }
+ , dstend = { 'U', { 0, 0, 0 }, 2 * 60 * 60 };
const char *_days[] = {
- "Sunday", "Monday", "Tuesday", "Wednesday",
- "Thursday", "Friday", "Saturday"
- };
+ "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday"
+ };
const char *_months[] = {
- "January", "February", "March",
- "April", "May", "June",
- "July", "August", "September",
- "October", "November", "December"
- };
+ "January", "February", "March",
+ "April", "May", "June",
+ "July", "August", "September",
+ "October", "November", "December"
+ };
const int _ytab[2][12] = {
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
-int
-_leap(int year)
+static const char *
+parseZoneName(register char *buf, register const char *p)
+{
+ register int n = 0;
+
+ if (*p == ':') return NULL;
+ while (*p && !isdigit(*p) && *p != ',' && *p != '-' && *p != '+') {
+ if (n < TZ_LEN)
+ *buf++ = *p;
+ p++;
+ n++;
+ }
+ if (n < 3) return NULL; /* error */
+ *buf = '\0';
+ return p;
+}
+
+static const char *
+parseTime(register long *tm, const char *p, register struct dsttype *dst)
{
- return (!(year % 4) && ((year % 100) || !(year % 400)));
+ register int n = 0;
+ register const char *q = p;
+ char ds_type = (dst ? dst->ds_type : '\0');
+
+ if (dst) dst->ds_type = 'U';
+
+ *tm = 0;
+ while(*p >= '0' && *p <= '9') {
+ n = 10 * n + (*p++ - '0');
+ }
+ if (q == p) return NULL; /* "The hour shall be required" */
+ if (n < 0 || n >= 24) return NULL;
+ *tm = n * 60 * 60;
+ if (*p == ':') {
+ p++;
+ n = 0;
+ while(*p >= '0' && *p <= '9') {
+ n = 10 * n + (*p++ - '0');
+ }
+ if (q == p) return NULL; /* format error */
+ if (n < 0 || n >= 60) return NULL;
+ *tm += n * 60;
+ if (*p == ':') {
+ p++;
+ n = 0;
+ while(*p >= '0' && *p <= '9') {
+ n = 10 * n + (*p++ - '0');
+ }
+ if (q == p) return NULL; /* format error */
+ if (n < 0 || n >= 60) return NULL;
+ *tm += n;
+ }
+ }
+ if (dst) {
+ dst->ds_type = ds_type;
+ dst->ds_sec = *tm;
+ }
+ return p;
}
-long
-_tzone(void)
+static const char *
+parseDate(register char *buf, register const char *p, struct dsttype *dstinfo)
{
- register char *p = getenv("TZ");
+ register const char *q;
register int n = 0;
- int sign = 1;
+ int cnt = 0;
+ const int bnds[3][2] = { { 1, 12 },
+ { 1, 5 },
+ { 0, 6}
+ };
+ char ds_type;
+
+ if (*p != 'M') {
+ if (*p == 'J') {
+ *buf++ = *p++;
+ ds_type = 'J';
+ }
+ else ds_type = 'Z';
+ q = p;
+ while(*p >= '0' && *p <= '9') {
+ n = 10 * n + (*p - '0');
+ *buf++ = *p++;
+ }
+ if (q == p) return NULL; /* format error */
+ if (n < (ds_type == 'J') || n > 365) return NULL;
+ dstinfo->ds_type = ds_type;
+ dstinfo->ds_date[0] = n;
+ return p;
+ }
+ ds_type = 'M';
+ do {
+ *buf++ = *p++;
+ q = p;
+ n = 0;
+ while(*p >= '0' && *p <= '9') {
+ n = 10 * n + (*p - '0');
+ *buf++ = *p++;
+ }
+ if (q == p) return NULL; /* format error */
+ if (n < bnds[cnt][0] || n > bnds[cnt][1]) return NULL;
+ dstinfo->ds_date[cnt] = n;
+ cnt++;
+ } while (cnt < 3 && *p == '.');
+ if (cnt != 3) return NULL;
+ *buf = '\0';
+ dstinfo->ds_type = ds_type;
+ return p;
+}
+
+static const char *
+parseRule(register char *buf, register const char *p)
+{
+ long time;
+ register const char *q;
+
+ if (!(p = parseDate(buf, p, &dststart))) return NULL;
+ buf += strlen(buf);
+ if (*p == '/') {
+ q = ++p;
+ if (!(p = parseTime(&time, p, &dststart))) return NULL;
+ while( p != q) *buf++ = *q++;
+ }
+ if (*p != ',') return NULL;
+ p++;
+ if (!(p = parseDate(buf, p, &dstend))) return NULL;
+ buf += strlen(buf);
+ if (*p == '/') {
+ q = ++p;
+ if (!(p = parseTime(&time, p, &dstend))) return NULL;
+ while(*buf++ = *q++);
+ }
+ if (*p) return NULL;
+ return p;
+}
+
+/* The following routine parses timezone information in POSIX-format. For
+ * the requirements, see IEEE Std 1003.1-1988 section 8.1.1.
+ * The function returns as soon as it spots an error.
+ */
+static void
+parseTZ(const char *p)
+{
+ register int n;
+ long tz, dst = 60 * 60, sign = 1;
+ static char lastTZ[2 * RULE_LEN];
+ static char buffer[RULE_LEN];
+
+ if (!p) return;
+
+ if (!strcmp(lastTZ, p)) return; /* nothing changed */
+
+ *_tzname[0] = '\0';
+ *_tzname[1] = '\0';
+ dststart.ds_type = 'U';
+ dststart.ds_sec = 2 * 60 * 60;
+ dstend.ds_type = 'U';
+ dstend.ds_sec = 2 * 60 * 60;
+
+ if (strlen(p) > 2 * RULE_LEN) return;
+ strcpy(lastTZ, p);
+
+ if (!(p = parseZoneName(buffer, p))) return;
+
+ if (*p == '-') {
+ sign = -1;
+ p++;
+ } else if (*p == '+') p++;
+
+ if (!(p = parseTime(&tz, p, NULL))) return;
+ tz *= sign;
+ _timezone = tz;
+ strncpy(_tzname[0], buffer, TZ_LEN);
+
+ if (!(_daylight = (*p != '\0'))) return;
+
+ buffer[0] = '\0';
+ if (!(p = parseZoneName(buffer, p))) return;
+ strncpy(_tzname[1], buffer, TZ_LEN);
+
+ buffer[0] = '\0';
+ if (*p && (*p != ','))
+ if (!(p = parseTime(&dst, p, NULL))) return;
+ _dst_off = dst; /* dst was initialized to 1 hour */
+ if (*p) {
+ if (*p != ',') return;
+ p++;
+ if (strlen(p) > RULE_LEN) return;
+ if (!(p = parseRule(buffer, p))) return;
+ }
+}
+
+void
+_tzset(void)
+{
+#if defined(__BSD4_2)
-#ifdef __BSD4_2
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
- __daylight = tz.tz_dsttime;
- __timezone = tz.tz_minuteswest * 60;
+ _daylight = tz.tz_dsttime;
+ _timezone = tz.tz_minuteswest * 60;
-#elif !defined(__USG)
+#elif !defined(_POSIX_SOURCE) && !defined(__USG)
+
+#if !defined(_MINIX) /* MINIX has no ftime() */
struct timeb time;
ftime(&time);
- __timezone = time.timezone * 60L;
- __daylight = time.dstflag;
+ _timezone = time.timezone * 60L;
+ _daylight = time.dstflag;
#endif
- if (p && *p) {
- strncpy(__tzname[0], p, 3);
- p += 3;
- if (*p == '-') {
- sign = -1;
- p++;
- }
- while (*p >= '0' && *p <= '9')
- n = 10 * n + (*p++ - '0');
- n *= sign;
- __timezone = ((long)(n * 60)) * 60;
- __daylight = (*p != '\0');
- strncpy(__tzname[1], p, 3);
- }
-#ifdef __USG
- timezone = __timezone;
- daylight = __daylight;
- tzname[0] = __tzname[0];
- tzname[1] = __tzname[1];
+#endif /* !_POSIX_SOURCE && !__USG */
+
+ parseTZ(getenv("TZ")); /* should go inside #if */
+
+#if defined(__USG) || defined(_POSIX_SOURCE)
+ tzname[0] = _tzname[0];
+ tzname[1] = _tzname[1];
+#if defined(__USG)
+ timezone = _timezone;
+ daylight = _daylight;
#endif
- return __timezone;
+#endif /* __USG || _POSIX_SOURCE */
}
static int
{
int first = FIRSTSUNDAY(timep);
- if (day >= 58 && LEAPYEAR(1900 + timep->tm_year)) day++;
+ if (day >= 58 && LEAPYEAR(YEAR0 + timep->tm_year)) day++;
if (day < first) return first;
return day - (day - first) % 7;
}
+static int
+date_of(register struct dsttype *dst, struct tm *timep)
+{
+ int leap = LEAPYEAR(YEAR0 + timep->tm_year);
+ int firstday, tmpday;
+ register int day, month;
+
+ if (dst->ds_type != 'M') {
+ return dst->ds_date[0] -
+ (dst->ds_type == 'J'
+ && leap
+ && dst->ds_date[0] < 58);
+ }
+ day = 0;
+ month = 1;
+ while (month < dst->ds_date[0]) {
+ day += _ytab[leap][month - 1];
+ month++;
+ }
+ firstday = (day + FIRSTDAYOF(timep)) % 7;
+ tmpday = day;
+ day += (dst->ds_date[2] - firstday + 7) % 7
+ + 7 * (dst->ds_date[1] - 1);
+ if (day >= tmpday + _ytab[leap][month]) day -= 7;
+ return day;
+}
+
unsigned
-_dstget(struct tm *timep)
+_dstget(register struct tm *timep)
{
int begindst, enddst;
+ register struct dsttype *dsts = &dststart, *dste = &dstend;
+
+ if (_daylight == -1)
+ _tzset();
+
+ timep->tm_isdst = _daylight;
+ if (!_daylight) return 0;
+
+ if (dsts->ds_type != 'U')
+ begindst = date_of(dsts, timep);
+ else begindst = last_sunday(89, timep); /* last Sun before Apr */
+ if (dste->ds_type != 'U')
+ enddst = date_of(dste, timep);
+ else enddst = last_sunday(272, timep); /* last Sun in Sep */
+
+ /* assume begindst != enddst (otherwise it would be no use) */
+ if (begindst < enddst) { /* northern hemisphere */
+ if (timep->tm_yday > begindst && timep->tm_yday < enddst)
+ return _dst_off;
+ } else { /* southern hemisphere */
+ if (timep->tm_yday > begindst || timep->tm_yday < enddst)
+ return _dst_off;
+ }
+
+ if (timep->tm_yday == begindst || timep->tm_yday == enddst) {
+ long dsttranssec; /* transition when day is this old */
+ long cursec;
+
+ if (timep->tm_yday == begindst)
+ dsttranssec = dsts->ds_sec;
+ else dsttranssec = dste->ds_sec;
+ cursec = ((timep->tm_hour * 60) + timep->tm_min) * 60
+ + timep->tm_sec;
- if(__daylight == -1)
- _tzone();
-
- timep->tm_isdst = __daylight;
- if (!__daylight) return 0;
- begindst = last_sunday(89, timep); /* last Sun before Apr */
- enddst = last_sunday(272, timep); /* last Sun in Sep */
-
- /* The rules for daylight saving time differ for different coutries.
- * Implemented here are heuristics that got it right in Holland for
- * the last couple of years. There is, of course, no universal
- * algorithm. Only tables would work perfect.
- */
- if ( (timep->tm_yday>begindst
- || (timep->tm_yday == begindst && timep->tm_hour >= 2))
- && (timep->tm_yday<enddst
- || (timep->tm_yday == enddst && timep->tm_hour < 3))) {
- /* it all happens between 2 and 3 */
-
- return 60*60;
+ if ((timep->tm_yday == begindst && cursec >= dsttranssec)
+ || (timep->tm_yday == enddst && cursec < dsttranssec))
+ return _dst_off;
}
timep->tm_isdst = 0;
#include <limits.h>
#include "loc_incl.h"
+/* The code assumes that time_t is an unsigned long. When it is not, some
+ * things may have to change.
+ */
time_t
mktime(register struct tm *timep)
{
- int day, year, month, yday;
- long tmp_sec, tz = _tzone();
- register time_t seconds, localseconds;
- int overflow = 0;
+ register long day, year;
+ register int tm_year;
+ int yday, month;
+ register time_t seconds;
+ int overflow;
unsigned dst;
-
timep->tm_min += timep->tm_sec / 60;
timep->tm_sec %= 60;
if (timep->tm_sec < 0) {
}
day += (timep->tm_mday - 1);
while (day < 0) {
- day += YEARSIZE(YEAR1 + timep->tm_year - 1);
+ day += YEARSIZE(YEAR0 + timep->tm_year - 1);
timep->tm_year--;
}
- while (day >= YEARSIZE(YEAR1 + timep->tm_year)) {
- day -= YEARSIZE(YEAR1 + timep->tm_year);
+ while (day >= YEARSIZE(YEAR0 + timep->tm_year)) {
+ day -= YEARSIZE(YEAR0 + timep->tm_year);
timep->tm_year++;
}
- while (day >= _ytab[LEAPYEAR(YEAR1 + timep->tm_year)][timep->tm_mon]) {
- day -= _ytab[LEAPYEAR(YEAR1 + timep->tm_year)][timep->tm_mon];
+ while (day >= _ytab[LEAPYEAR(YEAR0 + timep->tm_year)][timep->tm_mon]) {
+ day -= _ytab[LEAPYEAR(YEAR0 + timep->tm_year)][timep->tm_mon];
if (++(timep->tm_mon) == 12) {
timep->tm_mon = 0;
timep->tm_year++;
}
}
timep->tm_mday = day + 1;
- year = 70;
- if (timep->tm_year < year) return -1;
+ _tzset(); /* set timezone and dst info */
+ year = EPOCH_YR;
+ if (timep->tm_year < year - YEAR0) return -1;
seconds = 0;
- while (!overflow && year < timep->tm_year) {
- tmp_sec = SECS_DAY * YEARSIZE(YEAR1 + year);
- if (TIME_MAX - tmp_sec <= seconds) overflow++;
- else {
- seconds += tmp_sec;
- year++;
- }
- }
+ day = 0; /* means days since day 0 now */
+ overflow = 0;
+
+ /* Assume that when day becomes negative, there will certainly
+ * be overflow on seconds.
+ * The check for overflow needs not to be done for leapyears
+ * divisible by 400.
+ * The code only works when year (1970) is not a leapyear.
+ */
+#if EPOCH_YR != 1970
+#error EPOCH_YR != 1970
+#endif
+ tm_year = timep->tm_year + YEAR0;
+
+ if (LONG_MAX / 365 < tm_year - year) overflow++;
+ day = (tm_year - year) * 365;
+ if (LONG_MAX - day < (tm_year - year) / 4 + 1) overflow++;
+ day += (tm_year - year) / 4
+ + ((tm_year % 4) && tm_year % 4 < year % 4);
+ day -= (tm_year - year) / 100
+ + ((tm_year % 100) && tm_year % 100 < year % 100);
+ day += (tm_year - year) / 400
+ + ((tm_year % 400) && tm_year % 400 < year % 400);
+
yday = month = 0;
- while (!overflow && month < timep->tm_mon) {
- yday += _ytab[LEAPYEAR(YEAR1 + year)][month];
+ while (month < timep->tm_mon) {
+ yday += _ytab[LEAPYEAR(tm_year)][month];
month++;
}
yday += (timep->tm_mday - 1);
- if (!overflow) {
- tmp_sec = yday * SECS_DAY;
- if (TIME_MAX - tmp_sec <= seconds) overflow++;
- else seconds += tmp_sec;
- }
-
- timep->tm_yday = yday; /* ??? assignments should be later */
- /* day 0 was thursday (4) */
- timep->tm_wday = (seconds / SECS_DAY + 4) % 7;
+ if (day + yday < 0) overflow++;
+ day += yday;
- if (timep->tm_isdst < 0)
- dst = _dstget(timep);
- else if (timep->tm_isdst)
- dst = 60 * 60;
- else dst = 0;
+ timep->tm_yday = yday;
+ timep->tm_wday = (day + 4) % 7; /* day 0 was thursday (4) */
- if (!overflow) {
- tmp_sec = timep->tm_hour * 60 * 60;
- if (TIME_MAX - tmp_sec <= seconds) overflow++;
- else seconds += tmp_sec;
- }
+ seconds = ((timep->tm_hour * 60) + timep->tm_min) * 60 + timep->tm_sec;
- if (!overflow) {
- tmp_sec = timep->tm_min * 60;
- if (TIME_MAX - tmp_sec <= seconds) overflow++;
- else seconds += tmp_sec;
- }
+ if ((TIME_MAX - seconds) / SECS_DAY < day) overflow++;
+ seconds += day * SECS_DAY;
- if (!overflow) {
- tmp_sec = timep->tm_sec;
- if (TIME_MAX - tmp_sec <= seconds) overflow++;
- else seconds += tmp_sec;
- }
+ /* Now adjust according to timezone and daylight saving time */
- localseconds = seconds;
+ if (((_timezone > 0) && (TIME_MAX - _timezone < seconds))
+ || ((_timezone < 0) && (seconds < -_timezone)))
+ overflow++;
+ seconds += _timezone;
- if (!overflow) {
- tmp_sec = tz;
- if (((tmp_sec > 0) && (TIME_MAX - tmp_sec <= seconds))
- || ((tmp_sec < 0) && (seconds < -tmp_sec)))
- overflow++;
- else seconds += tmp_sec;
- }
+ if (timep->tm_isdst < 0)
+ dst = _dstget(timep);
+ else if (timep->tm_isdst)
+ dst = _dst_off;
+ else dst = 0;
- if (!overflow) {
- tmp_sec = dst;
- if (tmp_sec > seconds) overflow++;
- else seconds -= tmp_sec;
- }
+ if (dst > seconds) overflow++; /* dst is always non-negative */
+ seconds -= dst;
if (overflow) return (time_t)-1;