From: dana <dana@dana.is>
To: Oliver Kiddle <okiddle@yahoo.co.uk>
Cc: zsh-workers@zsh.org
Subject: Re: zsh/stat: output atime/mtime/ctime with nanoseconds
Date: Wed, 20 Jun 2018 00:12:31 -0500 [thread overview]
Message-ID: <103F2833-A38D-49C7-97D1-84EA299D9523@dana.is> (raw)
In-Reply-To: <16617.1529435823@thecus>
On 19 Jun 2018, at 14:17, Oliver Kiddle <okiddle@yahoo.co.uk> wrote:
>Checking back, it seems the patch in workers/24059 was never applied.
How is this? Building on the earlier patch, it:
* Adds a zgettime() function to conveniently get nanosecond-precision clock
time, or at least something approximating it (Peter had independently added ns
precision to zsh/datetime between then and now, so i figured it was best to
centralize that functionality)
* Updates ztrftime() to support %9. and %N
* Updates prompt expansion to format times with nanoseconds
* Updates zsh/datetime to use zgettime()
* Updates zsh/stat to format times with nanoseconds
* Updates documentation and tests
I'm not sure about the error-handling / fall-back stuff in zgettime(). Is an
error message necessary? I put one in because pws had one, but he wasn't falling
back to gettimeofday() like i am. Also the ret++/ret-- stuff may be a bit
strange; i couldn't think of anything cleaner.
One remaining (not new) issue is the fact that the strftime built-in doesn't
support sub-second times. That might not be feasible for `strftime -r`, but the
regular one could accept an optional third operand maybe?
dana
diff --git a/configure.ac b/configure.ac
index 7644ebe52..53c30c2cf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1113,6 +1113,14 @@ zsh_TYPE_EXISTS([
#endif
], struct timezone)
+dnl Check for struct timespec since POSIX only gained it in 2008
+zsh_TYPE_EXISTS([
+#define _GNU_SOURCE 1
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+], struct timespec)
+
dnl Check for utmp structures, for watch
zsh_TYPE_EXISTS([
#ifdef HAVE_SYS_TYPES_H
diff --git a/Src/zsh_system.h b/Src/zsh_system.h
index 5339b496f..8289ee97c 100644
--- a/Src/zsh_system.h
+++ b/Src/zsh_system.h
@@ -250,6 +250,14 @@ struct timezone {
};
#endif
+/* Used to provide compatibility with clock_gettime() */
+#if !defined(HAVE_STRUCT_TIMESPEC) && !defined(ZSH_OOT_MODULE)
+struct timespec {
+ time_t tv_sec;
+ long tv_nsec;
+};
+#endif
+
/* There's more than one non-standard way to get at this data */
#if !defined(HAVE_STRUCT_DIRENT_D_INO) && defined(HAVE_STRUCT_DIRENT_D_STAT)
# define d_ino d_stat.st_ino
diff --git a/Src/compat.c b/Src/compat.c
index a130d9264..7b5c4411c 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -94,6 +94,39 @@ gettimeofday(struct timeval *tv, struct timezone *tz)
#endif
+/* Provide clock time with nanoseconds */
+
+/**/
+mod_export int
+zgettime(struct timespec *ts)
+{
+ int ret = -1;
+
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec dts;
+ if (clock_gettime(CLOCK_REALTIME, &dts) < 0) {
+ zwarn("unable to retrieve time: %e", errno);
+ ret--;
+ } else {
+ ret++;
+ ts->tv_sec = (time_t) dts.tv_sec;
+ ts->tv_nsec = (long) dts.tv_nsec;
+ }
+#endif
+
+ if (ret) {
+ struct timeval dtv;
+ struct timezone dtz;
+ gettimeofday(&dtv, &dtz);
+ ret++;
+ ts->tv_sec = (time_t) dtv.tv_sec;
+ ts->tv_nsec = (long) dtv.tv_usec * 1000;
+ }
+
+ return ret;
+}
+
+
/* compute the difference between two calendar times */
/**/
diff --git a/Src/utils.c b/Src/utils.c
index b41851700..ee2ad207f 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -3224,7 +3224,7 @@ ztrftimebuf(int *bufsizeptr, int decr)
/**/
mod_export int
-ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
+ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long nsec)
{
int hr12;
#ifdef HAVE_STRFTIME
@@ -3299,15 +3299,15 @@ morefmt:
case '.':
if (ztrftimebuf(&bufsize, digs))
return -1;
- if (digs > 6)
- digs = 6;
- if (digs < 6) {
+ if (digs > 9)
+ digs = 9;
+ if (digs < 9) {
int trunc;
- for (trunc = 5 - digs; trunc; trunc--)
- usec /= 10;
- usec = (usec + 5) / 10;
+ for (trunc = 8 - digs; trunc; trunc--)
+ nsec /= 10;
+ nsec = (nsec + 8) / 10;
}
- sprintf(buf, "%0*ld", digs, usec);
+ sprintf(buf, "%0*ld", digs, nsec);
buf += digs;
break;
case '\0':
@@ -3369,6 +3369,12 @@ morefmt:
*buf++ = '0' + tm->tm_min / 10;
*buf++ = '0' + tm->tm_min % 10;
break;
+ case 'N':
+ if (ztrftimebuf(&bufsize, 9))
+ return -1;
+ sprintf(buf, "%09ld", nsec);
+ buf += 9;
+ break;
case 'S':
if (tm->tm_sec > 9 || !strip)
*buf++ = '0' + tm->tm_sec / 10;
diff --git a/Src/prompt.c b/Src/prompt.c
index 95da52559..959ed8e3d 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -273,8 +273,7 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
char *ss, *hostnam;
int t0, arg, test, sep, j, numjobs, len;
struct tm *tm;
- struct timezone dummy_tz;
- struct timeval tv;
+ struct timespec ts;
time_t timet;
Nameddir nd;
@@ -664,8 +663,8 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
tmfmt = "%l:%M%p";
break;
}
- gettimeofday(&tv, &dummy_tz);
- tm = localtime(&tv.tv_sec);
+ zgettime(&ts);
+ tm = localtime(&ts.tv_sec);
/*
* Hack because strftime won't say how
* much space it actually needs. Try to add it
@@ -675,7 +674,7 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
*/
for(j = 0, t0 = strlen(tmfmt)*8; j < 3; j++, t0*=2) {
addbufspc(t0);
- if ((len = ztrftime(bv->bp, t0, tmfmt, tm, tv.tv_usec))
+ if ((len = ztrftime(bv->bp, t0, tmfmt, tm, ts.tv_nsec))
>= 0)
break;
}
diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index 6e9047bc5..be378b347 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -180,66 +180,30 @@ getcurrentsecs(UNUSED(Param pm))
}
static double
-getcurrentrealtime(Param pm)
+getcurrentrealtime(UNUSED(Param pm))
{
-#ifdef HAVE_CLOCK_GETTIME
struct timespec now;
-
- if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
- zwarn("%s: unable to retrieve time: %e", pm->node.nam, errno);
- return (double)0.0;
- }
-
+ zgettime(&now);
return (double)now.tv_sec + (double)now.tv_nsec * 1e-9;
-#else
- struct timeval now;
- struct timezone dummy_tz;
-
- (void)pm;
- gettimeofday(&now, &dummy_tz);
-
- return (double)now.tv_sec + (double)now.tv_usec * 1e-6;
-#endif
}
static char **
-getcurrenttime(Param pm)
+getcurrenttime(UNUSED(Param pm))
{
char **arr;
char buf[DIGBUFSIZE];
-
-#ifdef HAVE_CLOCK_GETTIME
struct timespec now;
- if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
- zwarn("%s: unable to retrieve time: %e", pm->node.nam, errno);
- return NULL;
- }
-
- arr = (char **)zhalloc(3 * sizeof(*arr));
- sprintf(buf, "%ld", (long)now.tv_sec);
- arr[0] = dupstring(buf);
- sprintf(buf, "%ld", now.tv_nsec);
- arr[1] = dupstring(buf);
- arr[2] = NULL;
-
- return arr;
-#else
- struct timeval now;
- struct timezone dummy_tz;
-
- (void)pm;
- gettimeofday(&now, &dummy_tz);
+ zgettime(&now);
arr = (char **)zhalloc(3 * sizeof(*arr));
sprintf(buf, "%ld", (long)now.tv_sec);
arr[0] = dupstring(buf);
- sprintf(buf, "%ld", (long)now.tv_usec * 1000);
+ sprintf(buf, "%ld", (long)now.tv_nsec);
arr[1] = dupstring(buf);
arr[2] = NULL;
return arr;
-#endif
}
static struct builtin bintab[] = {
diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c
index 66baa1292..50a6a9bb2 100644
--- a/Src/Modules/stat.c
+++ b/Src/Modules/stat.c
@@ -188,7 +188,7 @@ static char *timefmt;
/**/
static void
-stattimeprint(time_t tim, char *outbuf, int flags)
+stattimeprint(time_t tim, long nsecs, char *outbuf, int flags)
{
if (flags & STF_RAW) {
sprintf(outbuf, "%ld", (unsigned long)tim);
@@ -199,7 +199,7 @@ stattimeprint(time_t tim, char *outbuf, int flags)
char *oend = outbuf + strlen(outbuf);
/* Where the heck does "40" come from? */
int len = ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) :
- localtime(&tim), 0L);
+ localtime(&tim), nsecs);
if (len > 0)
metafy(oend, len, META_NOALLOC);
if (flags & STF_RAW)
@@ -291,15 +291,27 @@ statprint(struct stat *sbuf, char *outbuf, char *fname, int iwhich, int flags)
break;
case ST_ATIM:
- stattimeprint(sbuf->st_atime, optr, flags);
+#ifdef GET_ST_ATIME_NSEC
+ stattimeprint(sbuf->st_atime, GET_ST_ATIME_NSEC(*sbuf), optr, flags);
+#else
+ stattimeprint(sbuf->st_atime, 0L, optr, flags);
+#endif
break;
case ST_MTIM:
- stattimeprint(sbuf->st_mtime, optr, flags);
+#ifdef GET_ST_MTIME_NSEC
+ stattimeprint(sbuf->st_mtime, GET_ST_MTIME_NSEC(*sbuf), optr, flags);
+#else
+ stattimeprint(sbuf->st_mtime, 0L, optr, flags);
+#endif
break;
case ST_CTIM:
- stattimeprint(sbuf->st_ctime, optr, flags);
+#ifdef GET_ST_CTIME_NSEC
+ stattimeprint(sbuf->st_ctime, GET_ST_CTIME_NSEC(*sbuf), optr, flags);
+#else
+ stattimeprint(sbuf->st_ctime, 0L, optr, flags);
+#endif
break;
case ST_BLKSIZE:
diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst
index 11f18dc71..56b7c294a 100644
--- a/Test/D01prompt.ztst
+++ b/Test/D01prompt.ztst
@@ -108,6 +108,14 @@
if (( $date2[7,8] != $date3[1,2] )); then
print "Years do not agree in $date2, $date3"
fi
+ # These are somewhat questionable, but...
+ ns=( ${="$(print -P '%D{%9.} %D{%N}')"} )
+ if [[ $ns[1] != [0-9](#c9) ]] || [[ $ns[2] != [0-9](#c9) ]]; then
+ print "Nanosecond lengths/formats are not as expected in $ns[1], $ns[2]"
+ fi
+ if (( ($ns2[2] - $ns[1]) > 5000000 )); then
+ print "Nanoseconds differ too much in $ns[1], $ns[2]"
+ fi
0:Dates produced by prompt escapes
mkdir foo
diff --git a/Doc/Zsh/prompt.yo b/Doc/Zsh/prompt.yo
index 3c8f2a094..909012c8e 100644
--- a/Doc/Zsh/prompt.yo
+++ b/Doc/Zsh/prompt.yo
@@ -198,11 +198,15 @@ endsitem()
In addition, if the system supports the POSIX tt(gettimeofday) system
call, tt(%.) provides decimal fractions of a second since the epoch with
leading zeroes. By default three decimal places are provided, but a
-number of digits up to 6 may be given following the tt(%); hence tt(%6.)
-outputs microseconds. A typical example of this is the format
-`tt(%D{%H:%M:%S.%.})'.
+number of digits up to 9 may be given following the tt(%); hence tt(%6.)
+outputs microseconds, and tt(%9.) outputs nanoseconds. (The latter
+requires a nanosecond-precision tt(clock_gettime); systems lacking this
+will return a value multiplied by the appropriate power of 10.) A typical
+example of this is the format `tt(%D{%H:%M:%S.%.})'.
-The GNU extension that a `tt(-)' between the tt(%) and the
+The GNU extension tt(%N) is handled as a synonym for tt(%9.).
+
+Additionally, the GNU extension that a `tt(-)' between the tt(%) and the
format character causes a leading zero or space to be stripped
is handled directly by the shell for the format characters tt(d), tt(f),
tt(H), tt(k), tt(l), tt(m), tt(M), tt(S) and tt(y); any other format
diff --git a/Doc/Zsh/mod_stat.yo b/Doc/Zsh/mod_stat.yo
index 96349061e..9caed1e45 100644
--- a/Doc/Zsh/mod_stat.yo
+++ b/Doc/Zsh/mod_stat.yo
@@ -114,7 +114,11 @@ named files; no list of file names is allowed in this case.
)
item(tt(-F) var(fmt))(
Supplies a tt(strftime) (see manref(strftime)(3)) string for the
-formatting of the time elements. The tt(-s) option is implied.
+formatting of the time elements. The format string supports all of the
+zsh extensions described in
+ifzman(the section EXPANSION OF PROMPT SEQUENCES in zmanref(zshmisc))\
+ifnzman(noderef(Prompt Expansion)).
+The tt(-s) option is implied.
)
item(tt(-g))(
Show the time elements in the GMT time zone. The
prev parent reply other threads:[~2018-06-20 5:12 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-06-19 14:18 Vincent Lefevre
2018-06-19 17:32 ` dana
2018-06-19 19:17 ` Oliver Kiddle
2018-06-20 5:12 ` dana [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=103F2833-A38D-49C7-97D1-84EA299D9523@dana.is \
--to=dana@dana.is \
--cc=okiddle@yahoo.co.uk \
--cc=zsh-workers@zsh.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).