From fdc5ce4a45ba240d51b1446cd73859d43e9a11c7 Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 4 Aug 1997 03:37:07 +0000 Subject: Add [-DWMY] flags for date adjustment. Discussed with: freebsd-hackers --- bin/date/Makefile | 4 +- bin/date/date.1 | 60 +++++++++- bin/date/date.c | 28 ++++- bin/date/vary.c | 337 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ bin/date/vary.h | 9 ++ 5 files changed, 430 insertions(+), 8 deletions(-) create mode 100644 bin/date/vary.c create mode 100644 bin/date/vary.h (limited to 'bin') diff --git a/bin/date/Makefile b/bin/date/Makefile index 294aae0..ed2bf8f 100644 --- a/bin/date/Makefile +++ b/bin/date/Makefile @@ -1,8 +1,8 @@ # @(#)Makefile 8.1 (Berkeley) 5/31/93 -# $Id$ +# $Id: Makefile,v 1.4 1997/02/22 14:02:30 peter Exp $ PROG= date -SRCS= date.c netdate.c +SRCS= date.c netdate.c vary.c DPADD= ${LIBUTIL} LDADD= -lutil diff --git a/bin/date/date.1 b/bin/date/date.1 index 9d02c09..7e8bf48 100644 --- a/bin/date/date.1 +++ b/bin/date/date.1 @@ -33,7 +33,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)date.1 8.3 (Berkeley) 4/28/95 -.\" $Id: date.1,v 1.11 1997/02/22 14:02:31 peter Exp $ +.\" $Id: date.1,v 1.12 1997/04/16 05:59:20 danny Exp $ .\" .Dd November 17, 1993 .Dt DATE 1 @@ -48,7 +48,8 @@ .Op Fl t Ar minutes_west .Op Fl nu .Op Cm + Ns Ar format -.Op [yy[mm[dd[hh]]]]mm[\&.ss] +.Op Fl DWMY Ar [+|-]val +.Op [[[[yy]mm]dd]hh]mm[\&.ss] .Sh DESCRIPTION .Nm displays the current date and time when invoked without arguments. @@ -97,6 +98,27 @@ by future calls to Display or set the date in .Tn UCT (universal) time. +.It Fl DWMY +Adjust the month day, week day, month or year according to +.Ar val . +If +.Ar val +is preceeded with a plus or minus sign, the date is adjusted forwards +or backwards according to the remaining string, otherwise the relevent +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 +Month days are in the range 1-31, week days are in the range 0-6 +(sun-sat), months are in the range 1-12 (jan-dec) and years are in +the range 80-38 or 1980-2038. +.Pp +The week day or month may be specified using a name rather than a +number. If a name is used with the plus (or minus) sign, the date +will be put forwards (or backwards) to the next (previous) 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 +Refer to the examples below for further details. .El .Pp An operand with a leading plus (``+'') sign signals a user-defined format @@ -150,6 +172,40 @@ TIME: 13:36:16 .Pp The command: .Bd -literal -offset indent +date -M1 -Y+1 +.Ed +.Pp +will display: +.Bd -literal -offset indent +Sun Jan 4 03:15:24 GMT 1998 +.Ed +.Pp +(where it is currently Mon Aug 4 04:15:24 BST 1997). +.Pp +The command: +.Bd -literal -offset indent +date -D1 -M3 -Y0 -D-1 +.Ed +.Pp +will display the last day of February in the year 2000: +.Bd -literal -offset indent +Tue Feb 29 03:18:00 GMT 2000 +.Ed +.Pp +The command: +.Bd -literal -offset indent +date -D1 -M+1 -D-1 -W-fri +.Ed +.Pp +will display the last friday of the month: +.Bd -literal -offset indent +Fri Aug 29 04:31:11 BST 1997 +.Ed +.Pp +(where it is currently Mon Aug 4 04:31:11 BST 1997). +.Pp +The command: +.Bd -literal -offset indent date 8506131627 .Ed .Pp diff --git a/bin/date/date.c b/bin/date/date.c index 8d95bda..ad9ffca 100644 --- a/bin/date/date.c +++ b/bin/date/date.c @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: date.c,v 1.12 1997/04/16 05:59:21 danny Exp $ + * $Id: date.c,v 1.13 1997/06/06 06:34:37 charnier Exp $ */ #ifndef lint @@ -57,6 +57,7 @@ static char const sccsid[] = "@(#)date.c 8.2 (Berkeley) 4/28/95"; #include #include "extern.h" +#include "vary.h" time_t tval; int retval, nflag; @@ -79,12 +80,16 @@ main(argc, argv) char *format, buf[1024]; char *endptr; int set_timezone; + struct vary *v; + const struct vary *badv; + struct tm lt; + v = NULL; (void) setlocale(LC_TIME, ""); tz.tz_dsttime = tz.tz_minuteswest = 0; rflag = 0; set_timezone = 0; - while ((ch = getopt(argc, argv, "d:nr:ut:")) != -1) + while ((ch = getopt(argc, argv, "D:W:M:Y:d:nr:ut:")) != -1) switch((char)ch) { case 'd': /* daylight savings time */ tz.tz_dsttime = strtol(optarg, &endptr, 10) ? 1 : 0; @@ -109,6 +114,12 @@ main(argc, argv) usage(); set_timezone = 1; break; + case 'D': + case 'W': + case 'M': + case 'Y': + v = vary_append(v, ch, optarg); + break; default: usage(); } @@ -141,7 +152,16 @@ main(argc, argv) if (*argv && **argv == '+') format = *argv + 1; - (void)strftime(buf, sizeof(buf), format, localtime(&tval)); + lt = *localtime(&tval); + badv = vary_apply(v, <); + if (badv) { + fprintf(stderr, "-%c %s: Cannot apply date adjustment\n", + badv->flag, badv->arg); + vary_destroy(v); + usage(); + } + vary_destroy(v); + (void)strftime(buf, sizeof(buf), format, <); (void)printf("%s\n", buf); exit(retval); } @@ -239,6 +259,6 @@ usage() { (void)fprintf(stderr, "%s\n%s\n", "usage: date [-nu] [-d dst] [-r seconds] [-t west] [+format]", - " [yy[mm[dd[hh]]]]mm[.ss]]"); + " [-DWMY [+|-]val] [[[[yy]mm]dd]HH]MM[.ss]]"); exit(1); } diff --git a/bin/date/vary.c b/bin/date/vary.c new file mode 100644 index 0000000..5df3a0c --- /dev/null +++ b/bin/date/vary.c @@ -0,0 +1,337 @@ +#include +#include +#include +#include "vary.h" + +struct trans { + int val; + char *str; +}; + +static struct trans trans_mon[] = { + { 1, "jan" }, { 2, "feb" }, { 3, "mar" }, { 4, "apr" }, { 5, "may" }, + { 6, "jun" }, { 7, "jul" }, { 8, "aug" }, { 9, "sep" }, { 10, "oct" }, + { 11, "nov" }, { 12, "dec" }, + { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" }, + { 6, "june" }, { 7, "july" }, { 8, "august" }, { 9, "september" }, + { 10, "october" }, { 11, "november" }, { 12, "december" }, + { -1, NULL } +}; + +static struct trans trans_wday[] = { + { 0, "sun" }, { 1, "mon" }, { 2, "tue" }, { 3, "wed" }, { 4, "thr" }, + { 4, "thu" }, { 5, "fri" }, { 6, "sat" }, + { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" }, + { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" }, + { -1, NULL } +}; + +static char digits[] = "0123456789"; + +static int +trans(const struct trans t[], const char *arg) +{ + int f; + + if (strspn(arg, digits) == strlen(arg)) + return atoi(arg); + + for (f = 0; t[f].val != -1; f++) + if (!strcasecmp(t[f].str, arg)) + return t[f].val; + + return -1; +} + +struct vary * +vary_append(struct vary *v, char flag, char *arg) +{ + struct vary *result, **nextp; + + if (!strchr("DWMY", flag)) + return 0; + + if (v) { + result = v; + while (v->next) + v = v->next; + nextp = &v->next; + } else + nextp = &result; + + *nextp = (struct vary *)malloc(sizeof(struct vary)); + (*nextp)->flag = flag; + (*nextp)->arg = arg; + (*nextp)->next = NULL; + return result; +} + +static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +static int +daysinmonth(const struct tm *t) +{ + int year; + + year = t->tm_year + 1900; + + if (t->tm_mon == 1) + if (!(year % 400)) + return 29; + else if (!(year % 100)) + return 28; + else if (!(year % 4)) + return 29; + else + return 28; + else if (t->tm_mon >= 0 && t->tm_mon < 12) + return mdays[t->tm_mon]; + + return 0; +} + + +static int +adjyear(struct tm *t, char type, int val) +{ + switch (type) { + case '+': + t->tm_year += val; + break; + case '-': + t->tm_year -= val; + break; + default: + t->tm_year = val; + if (t->tm_year < 69) + t->tm_year += 100; /* as per date.c */ + else if (t->tm_year > 1900) + t->tm_year -= 1900; /* struct tm holds years since 1900 */ + break; + } + return mktime(t) != -1; +} + +static int +sadjyear(struct tm *t, char *arg) +{ + switch (*arg) { + case '+': + case '-': + return adjyear(t, *arg, atoi(arg+1)); + default: + return adjyear(t, '\0', atoi(arg)); + } +} + +static int +adjmon(struct tm *t, char type, int val, int istext) +{ + if (val < 0) + return 0; + + switch (type) { + case '+': + if (istext) + if (val <= t->tm_mon) + val += 11 - t->tm_mon; /* early next year */ + 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; + break; + + case '-': + if (istext) + if (val-1 > t->tm_mon) + val = 13 - val + t->tm_mon; /* later last year */ + 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)) + return 0; + val -= 12; + } + t->tm_mon -= val; + break; + + default: + if (val > 12 || val < 1) + return 0; + t->tm_mon = --val; + } + + return mktime(t) != -1; +} + +static int +sadjmon(struct tm *t, char *arg) +{ + int istext; + int val; + + switch (*arg) { + case '+': + case '-': + istext = strspn(arg+1, digits) != strlen(arg+1); + val = trans(trans_mon, arg+1); + return adjmon(t, *arg, val, istext); + default: + istext = strspn(arg, digits) != strlen(arg); + val = trans(trans_mon, arg); + return adjmon(t, '\0', val, istext); + } +} + +static int +adjday(struct tm *t, char type, int val) +{ + int mdays; + switch (type) { + case '+': + while (val) { + mdays = daysinmonth(t); + if (val > mdays - t->tm_mday) { + val -= mdays - t->tm_mday + 1; + t->tm_mday = 1; + if (!adjmon(t, '+', 1, 0)) + return 0; + } else { + t->tm_mday += val; + val = 0; + } + } + break; + case '-': + while (val) + if (val >= t->tm_mday) { + val -= t->tm_mday; + t->tm_mday = 1; + if (!adjmon(t, '-', 1, 0)) + return 0; + t->tm_mday = daysinmonth(t); + } else { + t->tm_mday -= val; + val = 0; + } + break; + default: + if (val > 0 && val <= daysinmonth(t)) + t->tm_mday = val; + else + return 0; + break; + } + + return mktime(t) != -1; +} + +static int +sadjwday(struct tm *t, char *arg) +{ + int istext; + int val; + + switch (*arg) { + case '+': + case '-': + istext = strspn(arg+1, digits) != strlen(arg+1); + val = trans(trans_wday, arg+1); + break; + default: + istext = 0; + val = trans(trans_wday, arg); + break; + } + + if (val < 0) + return 0; + + switch (*arg) { + case '+': + if (istext) + if (val < t->tm_wday) + val = 7 - t->tm_wday + val; /* early next week */ + else + val -= t->tm_wday; /* later this week */ + else + val *= 7; /* "-W +5" == "5 weeks in the future" */ + return adjday(t, '+', val); + case '-': + 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); + default: + if (val < t->tm_wday) + return adjday(t, '-', t->tm_wday - val); + else if (val > 6) + return 0; + else if (val > t->tm_wday) + return adjday(t, '+', val - t->tm_wday); + } + return 1; +} + +static int +sadjday(struct tm *t, char *arg) +{ + switch (*arg) { + case '+': + case '-': + return adjday(t, *arg, atoi(arg+1)); + default: + return adjday(t, '\0', atoi(arg)); + } +} + +const struct vary * +vary_apply(const struct vary *v, struct tm *t) +{ + for (; v; v = v->next) { + switch (v->flag) { + case 'D': + if (!sadjday(t, v->arg)) + return v; + break; + case 'W': + if (!sadjwday(t, v->arg)) + return v; + break; + case 'M': + if (!sadjmon(t, v->arg)) + return v; + break; + case 'Y': + if (!sadjyear(t, v->arg)) + return v; + break; + default: + return v; + } + } + return 0; +} + +void +vary_destroy(struct vary *v) +{ + struct vary *n; + + while (v) { + n = v->next; + free(v); + v = n; + } +} diff --git a/bin/date/vary.h b/bin/date/vary.h new file mode 100644 index 0000000..146706f --- /dev/null +++ b/bin/date/vary.h @@ -0,0 +1,9 @@ +struct vary { + char flag; + char *arg; + struct vary *next; +}; + +extern struct vary *vary_append(struct vary *v, char flag, char *arg); +extern const struct vary *vary_apply(const struct vary *v, struct tm *t); +extern void vary_destroy(struct vary *v); -- cgit v1.1