Add [-DWMY] flags for date adjustment.

Discussed with: freebsd-hackers
This commit is contained in:
Brian Somers 1997-08-04 03:37:07 +00:00
parent b0ed9cd762
commit 7ca215a690
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=27874
5 changed files with 430 additions and 8 deletions

View File

@ -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

View File

@ -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

View File

@ -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 <locale.h>
#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, &lt);
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, &lt);
(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);
}

337
bin/date/vary.c Normal file
View File

@ -0,0 +1,337 @@
#include <time.h>
#include <string.h>
#include <stdlib.h>
#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;
}
}

9
bin/date/vary.h Normal file
View File

@ -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);