From 85b9c1f90b38b35ecf1d4c4e9a22bf122c2cef1c Mon Sep 17 00:00:00 2001 From: brian Date: Wed, 5 Apr 2000 01:59:36 +0000 Subject: Always give mktime a timeval with tm_isdst set to -1, otherwise things get a bit out-of-phase when we step backwards 1 hour from between 0:00 and 1:00 on the first of the month following the transition into Summer time. This is probably actually a bug in mktime(). PR: 10963 If mktime() fails and 68 < year < 138, assume that the reason is because of Summer time and adjust up or down according to our adjusting context by one hour. This assumes that all DSTs are multiples of 1 hour. PR: 6223, 17750 --- bin/date/vary.c | 181 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 117 insertions(+), 64 deletions(-) (limited to 'bin') diff --git a/bin/date/vary.c b/bin/date/vary.c index 97556fd..9063d38 100644 --- a/bin/date/vary.c +++ b/bin/date/vary.c @@ -53,6 +53,28 @@ static struct trans trans_wday[] = { }; static char digits[] = "0123456789"; +static int adjhour(struct tm *, char, int); + +static int +domktime(struct tm *t, char type) +{ + int ret; + + /* + * Don't let mktime have a specific tm_isdst value. If we do, + * it gets confused when we ``vary'' in and out of Summer time + */ + t->tm_isdst = -1; + if ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138) + /* + * If mktime() fails, adjust by an hour. If we're actually + * bridging a savings of several hours, we'll recurse several + * times. + */ + ret = adjhour(t, type == '-' ? type : '+', 1); + + return ret; +} static int trans(const struct trans t[], const char *arg) @@ -129,7 +151,7 @@ adjyear(struct tm *t, char type, int val) t->tm_year -= 1900; /* struct tm holds years since 1900 */ break; } - return mktime(t) != -1; + return domktime(t, type) != -1; } static int @@ -146,12 +168,14 @@ adjmon(struct tm *t, char type, int val, int istext) else val -= t->tm_mon + 1; /* later this year */ } - if (!adjyear(t, '+', (t->tm_mon + val) / 12)) - return 0; - val %= 12; - t->tm_mon += val; - if (t->tm_mon > 11) - t->tm_mon -= 12; + if (val) { + if (!adjyear(t, '+', (t->tm_mon + val) / 12)) + return 0; + val %= 12; + t->tm_mon += val; + if (t->tm_mon > 11) + t->tm_mon -= 12; + } break; case '-': @@ -161,15 +185,17 @@ adjmon(struct tm *t, char type, int val, int istext) else val = t->tm_mon - val + 1; /* early this year */ } - if (!adjyear(t, '-', val / 12)) - return 0; - val %= 12; - if (val > t->tm_mon) { - if (!adjyear(t, '-', 1)) + if (val) { + if (!adjyear(t, '-', val / 12)) return 0; - val -= 12; + val %= 12; + if (val > t->tm_mon) { + if (!adjyear(t, '-', 1)) + return 0; + val -= 12; + } + t->tm_mon -= val; } - t->tm_mon -= val; break; default: @@ -178,13 +204,14 @@ adjmon(struct tm *t, char type, int val, int istext) t->tm_mon = --val; } - return mktime(t) != -1; + return domktime(t, type) != -1; } static int adjday(struct tm *t, char type, int val) { int mdays; + switch (type) { case '+': while (val) { @@ -221,7 +248,7 @@ adjday(struct tm *t, char type, int val) break; } - return mktime(t) != -1; + return domktime(t, type) != -1; } static int @@ -238,17 +265,17 @@ adjwday(struct tm *t, char type, int val, int istext) else val -= t->tm_wday; /* later this week */ else - val *= 7; /* "-W +5" == "5 weeks in the future" */ - return adjday(t, '+', val); + val *= 7; /* "-v+5w" == "5 weeks in the future" */ + return !val || adjday(t, '+', val); case '-': - if (istext) + if (istext) { if (val > t->tm_wday) val = 7 - val + t->tm_wday; /* later last week */ else val = t->tm_wday - val; /* early this week */ - else - val *= 7; /* "-W -5" == "5 weeks ago" */ - return adjday(t, '-', val); + } else + val *= 7; /* "-v-5w" == "5 weeks ago" */ + return !val || adjday(t, '-', val); default: if (val < t->tm_wday) return adjday(t, '-', t->tm_wday - val); @@ -263,29 +290,47 @@ adjwday(struct tm *t, char type, int val, int istext) static int adjhour(struct tm *t, char type, int val) { + /* + * We've got to be careful here in case + * domktime() calls + * adjhour() calls + * adjday() calls + * domktime() calls + * adjhour() and recurses forever. It's vital that we've adjusted our + * number of hours before calling adjday(). + */ + if (val < 0) return 0; switch (type) { case '+': - if (!adjday(t, '+', (t->tm_hour + val) / 24)) - return 0; - val %= 24; - t->tm_hour += val; - if (t->tm_hour > 23) - t->tm_hour -= 24; + if (val) { + int days; + + days = (t->tm_hour + val) / 24; + val %= 24; + t->tm_hour += val; + t->tm_hour %= 24; + if (!adjday(t, '+', days)) + return 0; + } break; case '-': - if (!adjday(t, '-', val / 24)) - return 0; - val %= 24; - if (val > t->tm_hour) { - if (!adjday(t, '-', 1)) + if (val) { + int days; + + days = val / 24; + val %= 24; + if (val > t->tm_hour) { + days++; + val -= 24; + } + t->tm_hour -= val; + if (!adjday(t, '-', val / 24)) return 0; - val -= 24; } - t->tm_hour -= val; break; default: @@ -294,7 +339,7 @@ adjhour(struct tm *t, char type, int val) t->tm_hour = val; } - return mktime(t) != -1; + return domktime(t, type) != -1; } static int @@ -305,24 +350,28 @@ adjmin(struct tm *t, char type, int val) switch (type) { case '+': - if (!adjhour(t, '+', (t->tm_min + val) / 60)) - return 0; - val %= 60; - t->tm_min += val; - if (t->tm_min > 59) - t->tm_min -= 60; + if (val) { + if (!adjhour(t, '+', (t->tm_min + val) / 60)) + return 0; + val %= 60; + t->tm_min += val; + if (t->tm_min > 59) + t->tm_min -= 60; + } break; case '-': - if (!adjhour(t, '-', val / 60)) - return 0; - val %= 60; - if (val > t->tm_min) { - if (!adjhour(t, '-', 1)) + if (val) { + if (!adjhour(t, '-', val / 60)) return 0; - val -= 60; + val %= 60; + if (val > t->tm_min) { + if (!adjhour(t, '-', 1)) + return 0; + val -= 60; + } + t->tm_min -= val; } - t->tm_min -= val; break; default: @@ -331,7 +380,7 @@ adjmin(struct tm *t, char type, int val) t->tm_min = val; } - return mktime(t) != -1; + return domktime(t, type) != -1; } static int @@ -342,24 +391,28 @@ adjsec(struct tm *t, char type, int val) switch (type) { case '+': - if (!adjmin(t, '+', (t->tm_sec + val) / 60)) - return 0; - val %= 60; - t->tm_sec += val; - if (t->tm_sec > 59) - t->tm_sec -= 60; + if (val) { + if (!adjmin(t, '+', (t->tm_sec + val) / 60)) + return 0; + val %= 60; + t->tm_sec += val; + if (t->tm_sec > 59) + t->tm_sec -= 60; + } break; case '-': - if (!adjmin(t, '-', val / 60)) - return 0; - val %= 60; - if (val > t->tm_sec) { - if (!adjmin(t, '-', 1)) + if (val) { + if (!adjmin(t, '-', val / 60)) return 0; - val -= 60; + val %= 60; + if (val > t->tm_sec) { + if (!adjmin(t, '-', 1)) + return 0; + val -= 60; + } + t->tm_sec -= val; } - t->tm_sec -= val; break; default: @@ -368,7 +421,7 @@ adjsec(struct tm *t, char type, int val) t->tm_sec = val; } - return mktime(t) != -1; + return domktime(t, type) != -1; } const struct vary * -- cgit v1.1