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:
parent
541787e39f
commit
0883c48a66
@ -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
|
||||
|
114
bin/date/vary.c
114
bin/date/vary.c
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user