Change the behaviour of `-v' so that, e.g., stepping a month back

on March 31 won't take you to March 2 or 3 (now the result will
be the last day of February.)

In general, now stepping by months from the last days of the current
month A will take you to the very last day of the target month B if
B is shorter than A.

The previous version would just step to March 31 and rely on mktime(3)
to correct the date.  Despite its simplicity, such way was counter-intuitive
to users and caused pain to shell script writers.

Noticed by:	Igor Timkin <ivt at gamma dot ru>
Approved by:	brian
MFC after:	2 weeks
This commit is contained in:
Yaroslav Tykhiy 2004-08-09 13:43:39 +00:00
parent 8f0d968bd8
commit d3e240cb45
2 changed files with 30 additions and 1 deletions

View File

@ -31,7 +31,7 @@
.\" @(#)date.1 8.3 (Berkeley) 4/28/95
.\" $FreeBSD$
.\"
.Dd November 17, 1993
.Dd August 9, 2004
.Dt DATE 1
.Os
.Sh NAME
@ -218,6 +218,22 @@ When the date is adjusted to a specific value that occurs twice
the resulting timezone will be set so that the date matches the earlier of
the two times.
.Pp
Adjusting the date by months is inherently ambiguous because
a month is a unit of variable length depending on the current date.
This kind of date adjustment is applied in the most intuitive way.
First of all,
.Nm
tries to preserve the day of the month.
If it is impossible because the target month is shorter than the present one,
the last day of the target month will be the result.
For example, using
.Fl v No +1m
on May 31 will adjust the date to June 30, while using the same option
on January 30 will result in the date adjusted to the last day of February.
This approach is also believed to make the most sense for shell scripting.
Nevertheless, be aware that going forth and back by the same number of
months may take you to a different date.
.Pp
Refer to the examples below for further details.
.El
.Pp
@ -295,6 +311,12 @@ will display the last day of February in the year 2000:
.Pp
.Dl "Tue Feb 29 03:18:00 GMT 2000"
.Pp
So will do the command:
.Pp
.Dl "date -v30d -v3m -v0y -v-1m"
.Pp
because there is no such date as the 30th of February.
.Pp
The command:
.Pp
.Dl "date -v1d -v+1m -v-1d -v-fri"

View File

@ -148,6 +148,8 @@ adjyear(struct tm *t, char type, int val, int mk)
static int
adjmon(struct tm *t, char type, int val, int istext, int mk)
{
int lmdays;
if (val < 0)
return 0;
@ -195,6 +197,11 @@ adjmon(struct tm *t, char type, int val, int istext, int mk)
t->tm_mon = --val;
}
/* e.g., -v-1m on March, 31 is the last day of February in common sense */
lmdays = daysinmonth(t);
if (t->tm_mday > lmdays)
t->tm_mday = lmdays;
return !mk || domktime(t, type) != -1;
}