1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-09-02 22:11:54 +02:00

43075: Support nanosecond-precision time formatting

* Teach ztrftime() %9. and %N for nanoseconds
* Update prompt expansion to pass sub-second times for time formatting
* Update zsh/stat to pass sub-second times for atime/mtime/ctime

Patch heavily based on Oliver's earlier work @ workers/24059
This commit is contained in:
dana 2018-06-20 17:29:56 -05:00
parent eada7e1138
commit 394f3a47e4
11 changed files with 116 additions and 64 deletions

View file

@ -5,6 +5,12 @@
2018-06-20 dana <dana@dana.is>
* 43075 (based on 24059): Doc/Zsh/mod_stat.yo, Doc/Zsh/prompt.yo,
Src/Modules/datetime.c, Src/Modules/stat.c, Src/compat.c,
Src/prompt.c, Src/utils.c, Src/zsh_system.h, Test/D01prompt.ztst,
configure.ac: Support nanosecond precision in zsh/stat file times
and time formats generally (%9./%N)
* 43061: Completion/Darwin/Command/_open,
Completion/Unix/Command/_webbrowser: Improve open completion, adjust
_webbrowser wording

View file

@ -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

View file

@ -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

View file

@ -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;
}
zgettime(&now);
arr = (char **)zhalloc(3 * sizeof(*arr));
sprintf(buf, "%ld", (long)now.tv_sec);
arr[0] = dupstring(buf);
sprintf(buf, "%ld", now.tv_nsec);
sprintf(buf, "%ld", (long)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);
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);
arr[1] = dupstring(buf);
arr[2] = NULL;
return arr;
#endif
}
static struct builtin bintab[] = {

View file

@ -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:

View file

@ -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 */
/**/

View file

@ -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;
}

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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