On Wed, 28 Dec 2016 17:42:39 -0800
Michael Forney <[email protected]> wrote:
> Cool. Thanks for the new patch. I just have a few style nits.
> > eprintf("usage: %s [-u] [-d time] [+format]\n", argv0);
>
> Let's update this usage message.
Oops, I think I missed bringing that forward from my original patch.
> > +static int date_field(const char *s, size_t i)
>
> The suckless style guide says that `static int` should be on its own
> line (sorry, my suggestion had the error too).
No problem.
> > + t = time(NULL);
>
> This is pre-existing, but this call is not checked for errors.
Right. Done.
> > if (argc) {
> > - if (argc != 1 || argv[0][0] != '+')
> > + if (argc != 1)
> > usage();
> > - else
>
> This should use braces (since the else branch needs them).
Done.
>
> > + else if (argv[0][0] == '+') {
> > fmt = &argv[0][1];
> > + }
> > + else {
>
> These should be on the same line (`} else {`).
Done.
> > + date.tm_year = date_field(argv[0], 8);
> > + if (date.tm_year < 69)
> > + date.tm_year += now->tm_year < 69 ? 0 :
> > 100;
>
> I think this needs to be just `date.tm_year += 100` (regardless of the
> current year). POSIX says "If century is not specified, then values in
> the range [69,99] shall refer to years 1969 to 1999 inclusive, and
> values in the range [00,68] shall refer to years 2000 to 2068
> inclusive."
Done
> > + return fshut(stdout, "<stdout>");
>
> This is unnecessary since it never prints anything to stdout. Just `return 0`.
Done.
> Apart from these nits, this looks good to me.
>
Your help is greatly appreciated, thank you. Revised patch below.
Signed-off-by: John Vogel <[email protected]>
---
date.1 | 46 ++++++++++++++++++++++++++++++++++++++++++++--
date.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 99 insertions(+), 9 deletions(-)
diff --git a/date.1 b/date.1
index 29081a5..0171936 100644
--- a/date.1
+++ b/date.1
@@ -3,12 +3,15 @@
.Os sbase
.Sh NAME
.Nm date
-.Nd print date and time
+.Nd print or set date and time
.Sh SYNOPSIS
.Nm
.Op Fl d Ar time
.Op Fl u
.Op Cm + Ns Ar format
+.Sm off
+.Op Ar mmddHHMM Oo Oo Ar CC Oc Ar yy Oc
+.Sm on
.Sh DESCRIPTION
.Nm
prints the date and time according to
@@ -16,7 +19,8 @@ prints the date and time according to
or
.Ar format
using
-.Xr strftime 3 .
+.Xr strftime 3
+or sets the date.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl d Ar time
@@ -27,6 +31,44 @@ Unix epoch 1970-01-01T00:00:00Z.
.It Fl u
Print UTC time instead of local time.
.El
+.Pp
+An operand with a leading plus
+.Pq Cm +
+sign signals a user-defined format string using
+.Xr strftime 3
+conversion specifications.
+.Pp
+An operand without a leading plus sign is
+interpreted a value for setting the systems current date and time.
+The canonical representation for setting the date and time is:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar mm
+The month of the year, from 01 to 12.
+.It Ar dd
+The day of the month, from 01 to 31.
+.It Ar HH
+The hour of the day, from 00 to 23.
+.It Ar MM
+The minute of the hour, from 00 to 59.
+.It Ar CC
+The first two digits of the year (the century).
+.It Ar yy
+The second two digits of the year.
+If
+.Ar yy
+is specified, but
+.Ar CC
+is not, a value for
+.Ar yy
+between 69 and 99 results in a
+.Ar CC
+value of 19. Otherwise, a
+.Ar CC
+value of 20 is used.
+.El
+.Pp
+The century and year are optional. The default is the current year.
.Sh STANDARDS
The
.Nm
diff --git a/date.c b/date.c
index 1671e1f..4ebd010 100644
--- a/date.c
+++ b/date.c
@@ -1,6 +1,8 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
#include <time.h>
#include "util.h"
@@ -8,7 +10,13 @@
static void
usage(void)
{
- eprintf("usage: %s [-u] [-d time] [+format]\n", argv0);
+ eprintf("usage: %s [-u] [-d time] [+format] [mmddHHMM[[CC]yy]]\n",
argv0);
+}
+
+static int
+date_field(const char *s, size_t i)
+{
+ return (s[i] - '0') * 10 + (s[i+1] - '0');
}
int
@@ -19,8 +27,6 @@ main(int argc, char *argv[])
time_t t;
char buf[BUFSIZ], *fmt = "%c", *tz = "local";
- t = time(NULL);
-
ARGBEGIN {
case 'd':
t = estrtonum(EARGF(usage()), 0, LLONG_MAX);
@@ -33,14 +39,56 @@ main(int argc, char *argv[])
usage();
} ARGEND
+ t = time(NULL);
+ if (t == (time_t)-1)
+ eprintf("time failed: %s\n", strerror(errno));
+
+ if (!(now = tztime(&t)))
+ eprintf("%stime failed\n", tz);
+
if (argc) {
- if (argc != 1 || argv[0][0] != '+')
+ if (argc != 1) {
usage();
- else
+ } else if (argv[0][0] == '+') {
fmt = &argv[0][1];
+ } else {
+ struct tm date;
+ struct timespec ts;
+
+ date.tm_mon = date_field(argv[0], 0) - 1;
+ date.tm_mday = date_field(argv[0], 2);
+ date.tm_hour = date_field(argv[0], 4);
+ date.tm_min = date_field(argv[0], 6);
+
+ switch (strlen(argv[0])) {
+ case 8:
+ date.tm_year = now->tm_year;
+ break;
+ case 10:
+ date.tm_year = date_field(argv[0], 8);
+ if (date.tm_year < 69)
+ date.tm_year += 100;
+ break;
+ case 12:
+ date.tm_year = ((date_field(argv[0], 8) - 19) *
100) + date_field(argv[0], 10);
+ break;
+ default:
+ eprintf("invalid date format: %s\n", argv[0]);
+ }
+
+ t = mktime(&date);
+ if (t == (time_t)-1)
+ eprintf("mktime failed: bad calender date/time:
%s\n", argv[0]);
+
+ ts.tv_sec = t;
+ ts.tv_nsec = 0;
+
+ if (clock_settime(CLOCK_REALTIME, &ts) == -1)
+ eprintf("clock_settime failed: %s\n",
strerror(errno));
+
+ return 0;
+ }
}
- if (!(now = tztime(&t)))
- eprintf("%stime failed\n", tz);
strftime(buf, sizeof(buf), fmt, now);
puts(buf);