zsh-workers
 help / color / mirror / code / Atom feed
* [PATCH] Add nanosecond support to strftime (zsh/datetime)
@ 2018-11-07 22:19 dana
  2018-11-07 22:26 ` Bart Schaefer
  0 siblings, 1 reply; 3+ messages in thread
From: dana @ 2018-11-07 22:19 UTC (permalink / raw)
  To: Zsh workers

This is a follow-up to workers/43075, which added nanosecond support to (AFAIK)
the rest of the shell.

The strftime built-in is changed as follows:

1. An optional third operand (nanoseconds) is accepted to specify the nanosecond
   value to pass to ztrftime(). This is essentially patterned after the
   $epochtime array, and lets you do things like this:

   start=( $epochtime )
   # do some stuff
   strftime 'Started at %T.%3.' $start
   strftime 'Finished at %T.%3.' $epochtime

   I considered having it accept a float instead (so you could do like
   `strftime %N $EPOCHREALTIME`), but it seemed like this might be easier to
   deal with? Let me know if the other way's better though.

   If the third operand is *not* provided, the nanosecond value is assumed to be
   0 as before.

2. If the second operand (epochtime) is not provided, the current system time
   (with nanoseconds) is used automatically. There didn't really seem to be a
   *need* to provide the epoch time explicitly, so i thought this would be a
   nice convenience.

   Obviously this does mean that if the operand is elided, it'll now silently
   fall back to the current time. That's the only potential issue i can think
   of.

dana


diff --git a/Doc/Zsh/mod_datetime.yo b/Doc/Zsh/mod_datetime.yo
index 27bc78157..da65a9bbd 100644
--- a/Doc/Zsh/mod_datetime.yo
+++ b/Doc/Zsh/mod_datetime.yo
@@ -6,9 +6,13 @@ The tt(zsh/datetime) module makes available one builtin command:
 startitem()
 findex(strftime)
 cindex(date string, printing)
-xitem(tt(strftime) [ tt(-s) var(scalar) ] var(format) var(epochtime) )
+xitem(tt(strftime) [ tt(-s) var(scalar) ] var(format) [ var(epochtime) [ var(nanoseconds) ] ] )
 item(tt(strftime) tt(-r) [ tt(-q) ] [ tt(-s) var(scalar) ] var(format) var(timestring) )(
-Output the date denoted by var(epochtime) in the var(format) specified.
+Output the date in the var(format) specified.  With no var(epochtime), the
+current system date/time is used; optionally, var(epochtime) may be used to
+specify the number of seconds since the epoch, and var(nanoseconds) may
+additionally be used to specify the number of nanoseconds past the second
+(otherwise that number is assumed to be 0).
 See manref(strftime)(3) for details.  The zsh extensions described in
 ifzman(the section EXPANSION OF PROMPT SEQUENCES in zmanref(zshmisc))\
 ifnzman(noderef(Prompt Expansion)) are also available.

diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index be378b347..18c7fb58e 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -100,8 +100,8 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
 {
     int bufsize, x, len;
     char *endptr = NULL, *scalar = NULL, *buffer;
-    time_t secs;
-    struct tm *t;
+    struct tm *tm;
+    struct timespec ts;
 
     if (OPT_ISSET(ops,'s')) {
 	scalar = OPT_ARG(ops, 's');
@@ -110,30 +110,58 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
 	    return 1;
 	}
     }
-    if (OPT_ISSET(ops, 'r'))
+    if (OPT_ISSET(ops, 'r')) {
+	if (!argv[1]) {
+	    zwarnnam(nam, "timestring expected");
+	    return 1;
+	}
 	return reverse_strftime(nam, argv, scalar, OPT_ISSET(ops, 'q'));
-
-    errno = 0;
-    secs = (time_t)strtoul(argv[1], &endptr, 10);
-    if (errno != 0) {
-	zwarnnam(nam, "%s: %e", argv[1], errno);
-	return 1;
-    } else if (*endptr != '\0') {
-	zwarnnam(nam, "%s: invalid decimal number", argv[1]);
-	return 1;
     }
 
-    t = localtime(&secs);
-    if (!t) {
-	zwarnnam(nam, "%s: unable to convert to time", argv[1]);
-	return 1;
+    if (!argv[1]) {
+	zgettime(&ts);
+	tm = localtime(&ts.tv_sec);
+    } else {
+	errno = 0;
+
+	ts.tv_sec = (time_t)strtoul(argv[1], &endptr, 10);
+	if (errno != 0) {
+	    zwarnnam(nam, "%s: %e", argv[1], errno);
+	    return 1;
+	} else if (*argv[1] == '\0' || *endptr != '\0') {
+	    zwarnnam(nam, "%s: invalid decimal number", argv[1]);
+	    return 1;
+	}
+
+	tm = localtime(&ts.tv_sec);
+	if (!tm) {
+	    zwarnnam(nam, "%s: unable to convert to time", argv[1]);
+	    return 1;
+	}
+
+	ts.tv_nsec = 0L;
+	if (argv[2]) {
+	    ts.tv_nsec = (long)zstrtol(argv[2], &endptr, 10);
+	    if (errno != 0) {
+		zwarnnam(nam, "%s: %e", argv[2], errno);
+		return 1;
+	    } else if (*argv[2] == '\0' || *endptr != '\0') {
+		zwarnnam(nam, "%s: invalid decimal number", argv[2]);
+		return 1;
+	    } else if (ts.tv_nsec < 0) {
+		zwarnnam(nam, "%s: invalid nanosecond value", argv[2]);
+		return 1;
+	    }
+	}
     }
+
     bufsize = strlen(argv[0]) * 8;
     buffer = zalloc(bufsize);
 
     len = 0;
     for (x=0; x < 4; x++) {
-        if ((len = ztrftime(buffer, bufsize, argv[0], t, 0L)) >= 0 || x==3)
+        if ((len = ztrftime(buffer, bufsize, argv[0], tm, ts.tv_nsec)) >= 0 ||
+	    x==3)
 	    break;
 	buffer = zrealloc(buffer, bufsize *= 2);
     }
@@ -207,7 +235,7 @@ getcurrenttime(UNUSED(Param pm))
 }
 
 static struct builtin bintab[] = {
-    BUILTIN("strftime",    0, bin_strftime,    2,   2, 0, "qrs:", NULL),
+    BUILTIN("strftime",    0, bin_strftime,    1,   3, 0, "qrs:", NULL),
 };
 
 static const struct gsu_integer epochseconds_gsu =

diff --git a/Test/V09datetime.ztst b/Test/V09datetime.ztst
index ffad96c04..22d560750 100644
--- a/Test/V09datetime.ztst
+++ b/Test/V09datetime.ztst
@@ -82,3 +82,32 @@
 # The result can be '%@' (Linux), '@' (BSDs) or an error (Cygwin).
   [[ $(strftime '%@' 0 2> /dev/null) == (%|)@ || $? != 0 ]]
 0:bad format specifier
+
+# This test may fail at 23:59:59.xxx on New Year's Eve :/
+  [[ "$( strftime '%Y' )" == "$( strftime '%Y' "$EPOCHSECONDS" )" ]]
+0:epochtime optional
+
+  strftime '%Y-%m-%d %H:%M:%S.%3.' 1012615322
+  strftime '%Y-%m-%d %H:%M:%S.%3.' 1012615322 0
+  strftime '%Y-%m-%d %H:%M:%S.%3.' 1012615322 2
+  strftime '%Y-%m-%d %H:%M:%S.%3.' 1012615322 $(( 222 * (10 ** 9) ))
+0:optional nanoseconds
+>2002-02-02 02:02:02.000
+>2002-02-02 02:02:02.000
+>2002-02-02 02:02:02.000
+>2002-02-02 02:02:02.222
+
+  strftime '%Y' '' 2> /dev/null
+1:empty epochtime not allowed
+
+  strftime '%Y' 1012615322 '' 2> /dev/null
+1:empty nanoseconds not allowed
+
+  strftime '%N' 1012615322 ${(l<64><9>):-} 2> /dev/null
+1:overflowed nanoseconds not allowed
+
+  strftime '%N' 1012615322 -1 2> /dev/null
+1:negative nanoseconds not allowed
+
+  strftime -r '%Y' 2> /dev/null
+1:-r timestring not optional


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] Add nanosecond support to strftime (zsh/datetime)
  2018-11-07 22:19 [PATCH] Add nanosecond support to strftime (zsh/datetime) dana
@ 2018-11-07 22:26 ` Bart Schaefer
  2018-11-07 22:40   ` dana
  0 siblings, 1 reply; 3+ messages in thread
From: Bart Schaefer @ 2018-11-07 22:26 UTC (permalink / raw)
  To: dana; +Cc: zsh-workers

On Wed, Nov 7, 2018 at 2:19 PM dana <dana@dana.is> wrote:
>
> This is a follow-up to workers/43075, which added nanosecond support to (AFAIK)
> the rest of the shell.

I'm having flashbacks that a new format specifier for this may have
been discussed on the austin-group (POSIX standards) mailing list.  If
so, it would be preferable to match here whatever the (still
forthcoming?  I've lost track of austin-group recently) standard is
going to be, rather than invent something of our own.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] Add nanosecond support to strftime (zsh/datetime)
  2018-11-07 22:26 ` Bart Schaefer
@ 2018-11-07 22:40   ` dana
  0 siblings, 0 replies; 3+ messages in thread
From: dana @ 2018-11-07 22:40 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: zsh-workers

On 7 Nov 2018, at 16:26, Bart Schaefer <schaefer@brasslantern.com> wrote:
>I'm having flashbacks that a new format specifier for this may have
>been discussed on the austin-group (POSIX standards) mailing list.  If
>so, it would be preferable to match here whatever the (still
>forthcoming?  I've lost track of austin-group recently) standard is
>going to be, rather than invent something of our own.

I don't follow the mailing list, but i couldn't find any mention of it on their
bug tracker.

In any case, this change doesn't add any format specifiers, it just allows
strftime to pass nanoseconds to ztrftime(). The previous change extended the
existing %#. specifier so that you could do %9., and then as an alias for that
it also added %N, which is what GNU date (and anything else using Gnulib's
nstrftime()) uses. If there is something different that might be coming to
POSIX, i agree that it would be nice to support that too/instead. (Though the
previous change was already released, so if we replaced it we might be
breaking stuff...)

dana


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2018-11-07 22:40 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-07 22:19 [PATCH] Add nanosecond support to strftime (zsh/datetime) dana
2018-11-07 22:26 ` Bart Schaefer
2018-11-07 22:40   ` dana

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).