Re: [hackers] [sbase][Patch] date: add date/time setting capability
On Sun, 3 Sep 2017 13:17:57 -0700 Michael Forney wrote: > On 2017-03-09, John Vogel wrote: > > This tests good here: > > > > diff --git a/date.c b/date.c > > index dcf2528..67f28d0 100644 > > --- a/date.c > > +++ b/date.c > > @@ -25,7 +25,7 @@ datefield(const char *s, size_t i) > > static void > > setdate(const char *s, struct tm *now) > > { > > - struct tm date; > > + struct tm date = { .tm_sec = 0, .tm_isdst = -1 }; > > struct timespec ts; > > > > switch (strlen(s)) { > > Applied finally, with this and a couple more other minor tweaks. > Thanks for all your efforts. Very much appreciated.
Re: [hackers] [sbase][Patch] date: add date/time setting capability
On Thu, 9 Mar 2017 14:53:11 -0800 Michael Forney wrote: > On Thu, Mar 9, 2017 at 2:10 PM, John Vogel wrote: > > On Wed, 8 Mar 2017 09:47:15 -0800 > > Michael Forney wrote: > > > >> I pushed a slightly amended version of this patch to my sbase branch at > >> https://github.com/michaelforney/sbase/commit/4b13b23a689da5fc01c2a26affe8ad8087c777bf. > >> > > > > I am running into strange behavior in both this patch, as well as my last > > one. > > I am pretty sure I tested it before, but I am not convinced I did so > > thoroughly. > > When setting the date, sometimes the result is as expected, sometimes the > > result is wildly off. I have run it through gdb. The broke down time seems > > to > > be set as expected. When inspecting struct tm date in either my patch or > > this > > one, I notice garbage values in parts of the tm struct that are not being > > set. I have tried two methods for addressing: (1) zeroing the date variable > > with > > memset and (2) declaring struct tm date as static. Both methods do the trick > > and setting the date is consistent with input. I also considered explicitly > > zeroing the members that are not touched, but since not all of them seem to > > be > > required by posix, I thought that zeroing the whole struct made more sense. > > > > I think it is safe to just set tm_sec to 0, and tm_isdst to -1 (so > that mktime will attempt to guess whether or not DST is in effect). > POSIX says that tm_wday and tm_yday are ignored, and the glibc manual > (and musl implementation) says that the non-standard fields tm_gmtoff > and tm_zone are also ignored. > This tests good here: diff --git a/date.c b/date.c index dcf2528..67f28d0 100644 --- a/date.c +++ b/date.c @@ -25,7 +25,7 @@ datefield(const char *s, size_t i) static void setdate(const char *s, struct tm *now) { - struct tm date; + struct tm date = { .tm_sec = 0, .tm_isdst = -1 }; struct timespec ts; switch (strlen(s)) {
Re: [hackers] [sbase][Patch] date: add date/time setting capability
On Wed, 8 Mar 2017 09:47:15 -0800 Michael Forney wrote: > I pushed a slightly amended version of this patch to my sbase branch at > https://github.com/michaelforney/sbase/commit/4b13b23a689da5fc01c2a26affe8ad8087c777bf. > I am running into strange behavior in both this patch, as well as my last one. I am pretty sure I tested it before, but I am not convinced I did so thoroughly. When setting the date, sometimes the result is as expected, sometimes the result is wildly off. I have run it through gdb. The broke down time seems to be set as expected. When inspecting struct tm date in either my patch or this one, I notice garbage values in parts of the tm struct that are not being set. I have tried two methods for addressing: (1) zeroing the date variable with memset and (2) declaring struct tm date as static. Both methods do the trick and setting the date is consistent with input. I also considered explicitly zeroing the members that are not touched, but since not all of them seem to be required by posix, I thought that zeroing the whole struct made more sense.
Re: [hackers] [sbase][Patch] date: add date/time setting capability
On Sun, 1 Jan 2017 13:43:59 -0800 Michael Forney wrote: > On 12/29/16, John Vogel wrote: > > On Thu, 29 Dec 2016 12:11:33 +0100 > > Hiltjo Posthuma wrote: > > > >> Nitpicks: > >> - Check if digit in date_field(). > >> - Style: don't declare variables in the inner scope, just use the > >> function scope for struct tm date; and struct timespec ts; > >> > >> Kind regards, > >> Hiltjo > >> > > > > Thanks for your input. > > Hmm, I think your fix for this made the patch a bit awkward. I suggest > keeping the original structure, but changing date_field to eprintf > when it hits a non-digit, so you don't have to deal with all that > error checking in main. > Good point. Thank you. -- >8 -- date.1 | 46 +++-- date.c | 67 +++--- 2 files changed, 104 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..92ef9f4 100644 --- a/date.c +++ b/date.c @@ -1,14 +1,27 @@ /* See LICENSE file for copyright and license details. */ #include #include +#include +#include +#include #include #include "util.h" + 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) +{ + if (!isdigit(s[i]) || !isdigit(s[i+1])) + eprintf("invalid date format: %s\n", s); + + return (s[i] - '0') * 10 + (s[i+1] - '0'); } int @@ -16,11 +29,11 @@ main(int argc, char *argv[]) { struct tm *now; struct tm *(*tztime)(const time_t *) = localtime; + struct tm date; + struct timespec ts; 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 +46,54 @@ main(int argc, char *argv[]) usage(); } ARGEND + t = time(NULL); + if (t == (time_t)-1) + eprintf("time failed:"); + + 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 { + 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); +
Re: [hackers] [sbase][Patch] date: add date/time setting capability
On Thu, 29 Dec 2016 12:11:33 +0100 Hiltjo Posthuma wrote: > Nitpicks: > - Check if digit in date_field(). > - Style: don't declare variables in the inner scope, just use the > function scope for struct tm date; and struct timespec ts; > > Kind regards, > Hiltjo > Thanks for your input. -- >8 -- date.1 | 46 ++-- date.c | 75 +++--- 2 files changed, 112 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..bf377c7 100644 --- a/date.c +++ b/date.c @@ -1,6 +1,9 @@ /* See LICENSE file for copyright and license details. */ #include #include +#include +#include +#include #include #include "util.h" @@ -8,7 +11,15 @@ 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) +{ + if (isdigit(s[i]) && isdigit(s[i+1])) + return (s[i] - '0') * 10 + (s[i+1] - '0'); + return -1; } int @@ -16,11 +27,12 @@ main(int argc, char *argv[]) { struct tm *now; struct tm *(*tztime)(const time_t *) = localtime; + struct tm date; + struct timespec ts; time_t t; + int century, year; char buf[BUFSIZ], *fmt = "%c", *tz = "local"; - t = time(NULL); - ARGBEGIN { case 'd': t = estrtonum(EARGF(usage()), 0, LLONG_MAX); @@ -33,14 +45,63 @@ main(int argc, char *argv[]) usage(); } ARGEND + t = time(NULL); + if (t == (time_t)-1) + eprintf("time failed:"); + + 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 { + 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 >= 0) && (date.tm_year < 69)) + date.tm_year += 100; + break; + case 12: + century = date_field(argv[0], 8); + year = date_field(argv[0], 10); + if ((century >= 0) && (year >= 0)) { + date.tm_year = ((century - 19) * 100) + year; + break; + } + /* fallthrough */ + default: + date.tm_year = -1; + break; + } + + if ((date.tm_year < 0) || + ((date.tm_mon = date_field(argv[0], 0)) < 0) || + ((date.tm_mday = date_field(argv[0], 2)) < 0) || +
Re: [hackers] [sbase][Patch] date: add date/time setting capability
On Wed, 28 Dec 2016 21:38:51 -0800 Michael Forney wrote: > > + eprintf("time failed: %s\n", strerror(errno)); > > eprintf will print the error message and a newline if the format > string ends in ':'. > > So you can just do `eprintf("time:")`, which seems to be the > convention for sbase. Done. > > + if (clock_settime(CLOCK_REALTIME, &ts) == -1) > > + eprintf("clock_settime failed: %s\n", > > strerror(errno)); > > Same here. Done. -- >8 -- 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..1885190 100644 --- a/date.c +++ b/date.c @@ -1,6 +1,8 @@ /* See LICENSE file for copyright and license details. */ #include #include +#include +#include #include #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:"); + + 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.
Re: [hackers] [sbase][Patch] date: add date/time setting capability
On Wed, 28 Dec 2016 20:01:38 -0800 Evan Gates wrote: > For future reference, could I request use of scissors for this? Git am[0] > has a feature to cut off everything above a line containing only 8< (looks > like scissors) and dashes. e.g. > > 8<8< > > Keeping conversation above this line and patch bow it makes applying the > patch easier, no need to manually get rid of the rest of the email. > > Thanks > > [0] https://git-scm.com/docs/git-am Sounds reasonable. I use that convention in the future.
Re: [hackers] [sbase][Patch] date: add date/time setting capability
On Wed, 28 Dec 2016 17:42:39 -0800 Michael Forney 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, ""); > > 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 --- 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 #include +#include +#include #include #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
Re: [hackers] [sbase][Patch] date: add date/time setting capability
On Tue, 27 Dec 2016 17:34:02 -0800 Michael Forney wrote: > One suggestion I can think of, since the date format is simple and > uses fixed sizes, it to just parse into struct tm manually rather than > restructuring the date string into a new buffer with spaces. > > For example (untested): > static int date_field(char *s, size_t i) { > return (s[i] - '0') * 10 + (s[i+1] - '0'); > } > > char *s = argv[0]; > tm->tm_mon = date_field(s, 0) - 1; > tm->tm_day = date_field(s, 2); > tm->tm_hour = date_field(s, 4); > tm->tm_min = date_field(s, 6); > switch (len) { > case 8: > tm->tm_year = now->tm_year; > break; > case 10: > tm->tm_year = date_field(s, 8); > if (tm->tm_year < 69) > tm->tm_year += tm->year < 69 ? 2000 : 1900; > break; > case 12: > tm->tm_year = date_field(s, 8) * 100 + date_field(s, 10); > break; > } > Thanks for the help, Michael. I made a couple adjustments to account for tm_year needing to be 'years since 1900'. Tests fine here. -- date.1 | 46 -- date.c | 58 -- 2 files changed, 96 insertions(+), 8 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..4a18bd6 100644 --- a/date.c +++ b/date.c @@ -1,6 +1,8 @@ /* See LICENSE file for copyright and license details. */ #include #include +#include +#include #include #include "util.h" @@ -11,6 +13,11 @@ usage(void) eprintf("usage: %s [-u] [-d time] [+format]\n", argv0); } +static int date_field(const char *s, size_t i) +{ + return (s[i] - '0') * 10 + (s[i+1] - '0'); +} + int main(int argc, char *argv[]) { @@ -19,8 +26,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 +38,55 @@ main(int argc, char *argv[]) usage(); } ARGEND + t = time(NULL); + + 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 += now->tm_year < 69 ? 0 : 100; + break; + case 12: + date.tm_year = ((date_field(argv[0], 8) - 19) * 100) + date_field(argv[0], 10); + break; +
Re: [hackers] [sbase][Patch] date: add date/time setting capability
On Tue, 27 Dec 2016 12:20:41 +0100 Laslo Hunhold wrote: > I am unsure how to deal with this patch. I personally have an > NTD-daemon running and it does all my time-manipulations. I can imagine > that maybe manually setting your time is a good thing for debugging, > testing, joking, whatever, but is it really worth the added complexity? > > What do the others think? I need to set date during boot on my raspberrypis. Since posix specifies setting the date with date, I thought it would be helpful to add it to sbase. I can always supply my own date setting utility (which I do now) if the patch is deemed to bloat the code unduly.
[hackers] [sbase][Patch] date: add date/time setting capability
From 79079cdc560d68257c110482c730ab043c7f4c0f Mon Sep 17 00:00:00 2001 From: John Vogel Date: Thu, 5 May 2016 15:01:36 -0400 Subject: [sbase][Patch] date: add date/time setting capability To: hackers@suckless.org Signed-off-by: John Vogel --- date.1 | 46 -- date.c | 65 ++--- 2 files changed, 102 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..f3db89f 100644 --- a/date.c +++ b/date.c @@ -1,6 +1,8 @@ /* See LICENSE file for copyright and license details. */ #include #include +#include +#include #include #include "util.h" @@ -8,19 +10,20 @@ 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); } int main(int argc, char *argv[]) { struct tm *now; + struct tm date; struct tm *(*tztime)(const time_t *) = localtime; + struct timespec ts; time_t t; + size_t i, j, len; char buf[BUFSIZ], *fmt = "%c", *tz = "local"; - t = time(NULL); - ARGBEGIN { case 'd': t = estrtonum(EARGF(usage()), 0, LLONG_MAX); @@ -33,14 +36,62 @@ main(int argc, char *argv[]) usage(); } ARGEND + t = time(NULL); + + 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 { + len = strlen(argv[0]); + + switch (len) { + case 8: + fmt = "%m %d %H %M"; + break; + case 10: + fmt = "%m %d %H %M %y"; + break; + case 12: + fmt = "%m %d %H %M %C %y"; + break; + default: + eprintf("invalid date format: %s\n", argv[0]); + } + + /* strptime does not handle the mmddHHMM[[CC]yy] format with +* the leading zeroes for each specifier unless they are separated. +*/ + for (i = 0, j = 0; i < len; i++) { + buf[j++] = argv[0][i]; + if (i && i < len-1 && (i%2)) + buf[j++] = ' '; + } + buf[j] = '\0'; + + if (strptime(buf, fmt, &date) == NULL) + eprintf("strptime failed: invalid input: %s\n", argv[0]); + + if (len == 8) + date.tm_year = now->tm_year; + + t = mktime(&date); +