commit ed78aef5b180cf1d82f03e8ae43c62317f273962
Author:     John Vogel <[email protected]>
AuthorDate: Sun Jan 1 20:09:53 2017 -0500
Commit:     Michael Forney <[email protected]>
CommitDate: Sun Sep 3 12:36:15 2017 -0700

    date: add date/time setting capability
    
    [Michael Forney: Moved functionality to setdate, other minor tweaks]

diff --git a/date.1 b/date.1
index 29081a5..8e6dd1a 100644
--- a/date.1
+++ b/date.1
@@ -3,12 +3,17 @@
 .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
+.Oo
+.Cm + Ns Ar format |
+.Sm off
+.Ar mmddHHMM Oo Oo Ar CC Oc Ar yy Oc
+.Sm on
+.Oc
 .Sh DESCRIPTION
 .Nm
 prints the date and time according to
@@ -16,7 +21,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
@@ -25,8 +31,46 @@ Print
 given as the number of seconds since the
 Unix epoch 1970-01-01T00:00:00Z.
 .It Fl u
-Print UTC time instead of local time.
+Print or set 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 as a value
+for setting the system's 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 7201227..97c910c 100644
--- a/date.c
+++ b/date.c
@@ -1,6 +1,8 @@
 /* See LICENSE file for copyright and license details. */
+#include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <time.h>
 
 #include "util.h"
@@ -8,7 +10,55 @@
 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
+datefield(const char *s, size_t i)
+{
+       if (!isdigit(s[i]) || !isdigit(s[i+1]))
+               eprintf("invalid date format: %s\n", s);
+
+       return (s[i] - '0') * 10 + (s[i+1] - '0');
+}
+
+static void
+setdate(const char *s, struct tm *now)
+{
+       struct tm date;
+       struct timespec ts;
+
+       switch (strlen(s)) {
+       case 8:
+               date.tm_year = now->tm_year;
+               break;
+       case 10:
+               date.tm_year = datefield(s, 8);
+               if (date.tm_year < 69)
+                       date.tm_year += 100;
+               break;
+       case 12:
+               date.tm_year = ((datefield(s, 8) - 19) * 100) + datefield(s, 
10);
+               break;
+       default:
+               eprintf("invalid date format: %s\n", s);
+               break;
+       }
+
+       date.tm_mon = datefield(s, 0) - 1;
+       date.tm_mday = datefield(s, 2);
+       date.tm_hour = datefield(s, 4);
+       date.tm_min = datefield(s, 6);
+       date.tm_sec = 0;
+       date.tm_isdst = -1;
+
+       ts.tv_sec = mktime(&date);
+       if (ts.tv_sec == -1)
+               eprintf("mktime:");
+       ts.tv_nsec = 0;
+
+       if (clock_settime(CLOCK_REALTIME, &ts) == -1)
+               eprintf("clock_settime:");
 }
 
 int
@@ -19,6 +69,8 @@ main(int argc, char *argv[])
        char buf[BUFSIZ], *fmt = "%c";
 
        t = time(NULL);
+       if (t == -1)
+               eprintf("time:");
 
        ARGBEGIN {
        case 'd':
@@ -32,14 +84,17 @@ main(int argc, char *argv[])
                usage();
        } ARGEND
 
+       if (!(now = localtime(&t)))
+               eprintf("localtime:");
        if (argc) {
-               if (argc != 1 || argv[0][0] != '+')
+               if (argc != 1)
                        usage();
-               else
-                       fmt = &argv[0][1];
+               if (argv[0][0] != '+') {
+                       setdate(argv[0], now);
+                       return 0;
+               }
+               fmt = &argv[0][1];
        }
-       if (!(now = localtime(&t)))
-               eprintf("localtime:");
 
        strftime(buf, sizeof(buf), fmt, now);
        puts(buf);

Reply via email to