From d3e240cb4503475c3a5ccbf957ed12a135a102ce Mon Sep 17 00:00:00 2001 From: Yaroslav Tykhiy Date: Mon, 9 Aug 2004 13:43:39 +0000 Subject: [PATCH] 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 Approved by: brian MFC after: 2 weeks --- bin/date/date.1 | 24 +++++++++++++++++++++++- bin/date/vary.c | 7 +++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/bin/date/date.1 b/bin/date/date.1 index 917c5cd75fa4..c588489603de 100644 --- a/bin/date/date.1 +++ b/bin/date/date.1 @@ -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" diff --git a/bin/date/vary.c b/bin/date/vary.c index a314e4823ee1..5f0123110ee3 100644 --- a/bin/date/vary.c +++ b/bin/date/vary.c @@ -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; }