Change -v so that variences in units of days or less are sensitive to

DST.  Explain in more detail what date -v does when mixed with DST.

Previous code suggested broken by: wollman
This commit is contained in:
brian 2000-04-12 13:35:17 +00:00
parent 541787e39f
commit 0883c48a66
2 changed files with 75 additions and 64 deletions

View File

@ -143,7 +143,9 @@ or backwards according to the remaining string, otherwise the relevant
part of the date is set. The date can be adjusted as many times as
required using these flags. Flags are processed in the order given.
.Pp
Seconds are in the range 0-59, minutes are in the range 0-59, hours are
When setting values
.Pq rather than adjusting them ,
seconds are in the range 0-59, minutes are in the range 0-59, hours are
in the range 1-12, month days are in the range 1-31, week days are in the
range 0-6
.Pq Sun-Sat ,
@ -174,6 +176,27 @@ to the next
date that matches the given week day or month. This will not adjust the date
if the given week day or month is the same as the current one.
.Pp
When a date is adjusted to a specific value or in units greater than hours,
daylight savings time considerations are ignored.
Adjustments in units of hours or less honour daylight savings time.
So, assuming the current date is March 26, 0:30 and that the DST adjustment
means that the clock goes forward at 01:00 to 02:00, using
.Fl v No +1H
will adjust the date to March 26, 2:30.
Likewise, if the date is October 29, 0:30 and the DST adjustment means that
the clock goes back at 02:00 to 01:00, using
.Fl v No +3H
will be necessary to reach October 20, 2:30.
.Pp
When the date is adjusted to a specific value that doesn't actually exist
.Pq for example March 26, 1:30 BST 2000 ,
the date will be silently adjusted forwards in units of one hour until it
reaches a valid time.
When the date is adjusted to a specific value that occurs twice
.Pq for example October 29, 1:30 2000 ,
the resulting timezone will be set so that the date matches the earlier of
the two times.
.Pp
Refer to the examples below for further details.
.El
.Pp

View File

@ -53,25 +53,16 @@ static struct trans trans_wday[] = {
};
static char digits[] = "0123456789";
static int adjhour(struct tm *, char, int);
static int adjhour(struct tm *, char, int, int);
static int
domktime(struct tm *t, char type)
{
int ret;
time_t 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);
while ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138)
/* While mktime() fails, adjust by an hour */
adjhour(t, type == '-' ? type : '+', 1, 0);
return ret;
}
@ -134,7 +125,7 @@ daysinmonth(const struct tm *t)
static int
adjyear(struct tm *t, char type, int val)
adjyear(struct tm *t, char type, int val, int mk)
{
switch (type) {
case '+':
@ -151,11 +142,11 @@ adjyear(struct tm *t, char type, int val)
t->tm_year -= 1900; /* struct tm holds years since 1900 */
break;
}
return domktime(t, type) != -1;
return !mk || domktime(t, type) != -1;
}
static int
adjmon(struct tm *t, char type, int val, int istext)
adjmon(struct tm *t, char type, int val, int istext, int mk)
{
if (val < 0)
return 0;
@ -169,7 +160,7 @@ adjmon(struct tm *t, char type, int val, int istext)
val -= t->tm_mon + 1; /* later this year */
}
if (val) {
if (!adjyear(t, '+', (t->tm_mon + val) / 12))
if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0))
return 0;
val %= 12;
t->tm_mon += val;
@ -186,11 +177,11 @@ adjmon(struct tm *t, char type, int val, int istext)
val = t->tm_mon - val + 1; /* early this year */
}
if (val) {
if (!adjyear(t, '-', val / 12))
if (!adjyear(t, '-', val / 12, 0))
return 0;
val %= 12;
if (val > t->tm_mon) {
if (!adjyear(t, '-', 1))
if (!adjyear(t, '-', 1, 0))
return 0;
val -= 12;
}
@ -204,11 +195,11 @@ adjmon(struct tm *t, char type, int val, int istext)
t->tm_mon = --val;
}
return domktime(t, type) != -1;
return !mk || domktime(t, type) != -1;
}
static int
adjday(struct tm *t, char type, int val)
adjday(struct tm *t, char type, int val, int mk)
{
int mdays;
@ -219,7 +210,7 @@ adjday(struct tm *t, char type, int val)
if (val > mdays - t->tm_mday) {
val -= mdays - t->tm_mday + 1;
t->tm_mday = 1;
if (!adjmon(t, '+', 1, 0))
if (!adjmon(t, '+', 1, 0, 0))
return 0;
} else {
t->tm_mday += val;
@ -232,7 +223,7 @@ adjday(struct tm *t, char type, int val)
if (val >= t->tm_mday) {
val -= t->tm_mday;
t->tm_mday = 1;
if (!adjmon(t, '-', 1, 0))
if (!adjmon(t, '-', 1, 0, 0))
return 0;
t->tm_mday = daysinmonth(t);
} else {
@ -248,11 +239,11 @@ adjday(struct tm *t, char type, int val)
break;
}
return domktime(t, type) != -1;
return !mk || domktime(t, type) != -1;
}
static int
adjwday(struct tm *t, char type, int val, int istext)
adjwday(struct tm *t, char type, int val, int istext, int mk)
{
if (val < 0)
return 0;
@ -266,7 +257,7 @@ adjwday(struct tm *t, char type, int val, int istext)
val -= t->tm_wday; /* later this week */
else
val *= 7; /* "-v+5w" == "5 weeks in the future" */
return !val || adjday(t, '+', val);
return !val || adjday(t, '+', val, 0);
case '-':
if (istext) {
if (val > t->tm_wday)
@ -275,31 +266,21 @@ adjwday(struct tm *t, char type, int val, int istext)
val = t->tm_wday - val; /* early this week */
} else
val *= 7; /* "-v-5w" == "5 weeks ago" */
return !val || adjday(t, '-', val);
return !val || adjday(t, '-', val, 0);
default:
if (val < t->tm_wday)
return adjday(t, '-', t->tm_wday - val);
return adjday(t, '-', t->tm_wday - val, 0);
else if (val > 6)
return 0;
else if (val > t->tm_wday)
return adjday(t, '+', val - t->tm_wday);
return adjday(t, '+', val - t->tm_wday, 0);
}
return 1;
}
static int
adjhour(struct tm *t, char type, int val)
adjhour(struct tm *t, char type, int val, int mk)
{
/*
* 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;
@ -312,7 +293,7 @@ adjhour(struct tm *t, char type, int val)
val %= 24;
t->tm_hour += val;
t->tm_hour %= 24;
if (!adjday(t, '+', days))
if (!adjday(t, '+', days, 0))
return 0;
}
break;
@ -328,7 +309,7 @@ adjhour(struct tm *t, char type, int val)
val -= 24;
}
t->tm_hour -= val;
if (!adjday(t, '-', val / 24))
if (!adjday(t, '-', days, 0))
return 0;
}
break;
@ -339,11 +320,11 @@ adjhour(struct tm *t, char type, int val)
t->tm_hour = val;
}
return domktime(t, type) != -1;
return !mk || domktime(t, type) != -1;
}
static int
adjmin(struct tm *t, char type, int val)
adjmin(struct tm *t, char type, int val, int mk)
{
if (val < 0)
return 0;
@ -351,7 +332,7 @@ adjmin(struct tm *t, char type, int val)
switch (type) {
case '+':
if (val) {
if (!adjhour(t, '+', (t->tm_min + val) / 60))
if (!adjhour(t, '+', (t->tm_min + val) / 60, 0))
return 0;
val %= 60;
t->tm_min += val;
@ -362,11 +343,11 @@ adjmin(struct tm *t, char type, int val)
case '-':
if (val) {
if (!adjhour(t, '-', val / 60))
if (!adjhour(t, '-', val / 60, 0))
return 0;
val %= 60;
if (val > t->tm_min) {
if (!adjhour(t, '-', 1))
if (!adjhour(t, '-', 1, 0))
return 0;
val -= 60;
}
@ -380,11 +361,11 @@ adjmin(struct tm *t, char type, int val)
t->tm_min = val;
}
return domktime(t, type) != -1;
return !mk || domktime(t, type) != -1;
}
static int
adjsec(struct tm *t, char type, int val)
adjsec(struct tm *t, char type, int val, int mk)
{
if (val < 0)
return 0;
@ -392,7 +373,7 @@ adjsec(struct tm *t, char type, int val)
switch (type) {
case '+':
if (val) {
if (!adjmin(t, '+', (t->tm_sec + val) / 60))
if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0))
return 0;
val %= 60;
t->tm_sec += val;
@ -403,11 +384,11 @@ adjsec(struct tm *t, char type, int val)
case '-':
if (val) {
if (!adjmin(t, '-', val / 60))
if (!adjmin(t, '-', val / 60, 0))
return 0;
val %= 60;
if (val > t->tm_sec) {
if (!adjmin(t, '-', 1))
if (!adjmin(t, '-', 1, 0))
return 0;
val -= 60;
}
@ -421,7 +402,7 @@ adjsec(struct tm *t, char type, int val)
t->tm_sec = val;
}
return domktime(t, type) != -1;
return !mk || domktime(t, type) != -1;
}
const struct vary *
@ -444,15 +425,18 @@ vary_apply(const struct vary *v, struct tm *t)
if (len < 2)
return v;
if (type == '\0')
t->tm_isdst = -1;
if (strspn(arg, digits) != len-1) {
val = trans(trans_wday, arg);
if (val != -1) {
if (!adjwday(t, type, val, 1))
if (!adjwday(t, type, val, 1, 1))
return v;
} else {
val = trans(trans_mon, arg);
if (val != -1) {
if (!adjmon(t, type, val, 1))
if (!adjmon(t, type, val, 1, 1))
return v;
} else
return v;
@ -463,31 +447,35 @@ vary_apply(const struct vary *v, struct tm *t)
switch (which) {
case 'S':
if (!adjsec(t, type, val))
if (!adjsec(t, type, val, 1))
return v;
break;
case 'M':
if (!adjmin(t, type, val))
if (!adjmin(t, type, val, 1))
return v;
break;
case 'H':
if (!adjhour(t, type, val))
if (!adjhour(t, type, val, 1))
return v;
break;
case 'd':
if (!adjday(t, type, val))
t->tm_isdst = -1;
if (!adjday(t, type, val, 1))
return v;
break;
case 'w':
if (!adjwday(t, type, val, 0))
t->tm_isdst = -1;
if (!adjwday(t, type, val, 0, 1))
return v;
break;
case 'm':
if (!adjmon(t, type, val, 0))
t->tm_isdst = -1;
if (!adjmon(t, type, val, 0, 1))
return v;
break;
case 'y':
if (!adjyear(t, type, val))
t->tm_isdst = -1;
if (!adjyear(t, type, val, 1))
return v;
break;
default: