mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-09-17 15:01:40 +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:
parent
eada7e1138
commit
394f3a47e4
11 changed files with 116 additions and 64 deletions
|
@ -5,6 +5,12 @@
|
||||||
|
|
||||||
2018-06-20 dana <dana@dana.is>
|
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,
|
* 43061: Completion/Darwin/Command/_open,
|
||||||
Completion/Unix/Command/_webbrowser: Improve open completion, adjust
|
Completion/Unix/Command/_webbrowser: Improve open completion, adjust
|
||||||
_webbrowser wording
|
_webbrowser wording
|
||||||
|
|
|
@ -114,7 +114,11 @@ named files; no list of file names is allowed in this case.
|
||||||
)
|
)
|
||||||
item(tt(-F) var(fmt))(
|
item(tt(-F) var(fmt))(
|
||||||
Supplies a tt(strftime) (see manref(strftime)(3)) string for the
|
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))(
|
item(tt(-g))(
|
||||||
Show the time elements in the GMT time zone. The
|
Show the time elements in the GMT time zone. The
|
||||||
|
|
|
@ -198,11 +198,15 @@ endsitem()
|
||||||
In addition, if the system supports the POSIX tt(gettimeofday) system
|
In addition, if the system supports the POSIX tt(gettimeofday) system
|
||||||
call, tt(%.) provides decimal fractions of a second since the epoch with
|
call, tt(%.) provides decimal fractions of a second since the epoch with
|
||||||
leading zeroes. By default three decimal places are provided, but a
|
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.)
|
number of digits up to 9 may be given following the tt(%); hence tt(%6.)
|
||||||
outputs microseconds. A typical example of this is the format
|
outputs microseconds, and tt(%9.) outputs nanoseconds. (The latter
|
||||||
`tt(%D{%H:%M:%S.%.})'.
|
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
|
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),
|
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
|
tt(H), tt(k), tt(l), tt(m), tt(M), tt(S) and tt(y); any other format
|
||||||
|
|
|
@ -180,66 +180,30 @@ getcurrentsecs(UNUSED(Param pm))
|
||||||
}
|
}
|
||||||
|
|
||||||
static double
|
static double
|
||||||
getcurrentrealtime(Param pm)
|
getcurrentrealtime(UNUSED(Param pm))
|
||||||
{
|
{
|
||||||
#ifdef HAVE_CLOCK_GETTIME
|
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
|
zgettime(&now);
|
||||||
if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
|
|
||||||
zwarn("%s: unable to retrieve time: %e", pm->node.nam, errno);
|
|
||||||
return (double)0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (double)now.tv_sec + (double)now.tv_nsec * 1e-9;
|
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 **
|
static char **
|
||||||
getcurrenttime(Param pm)
|
getcurrenttime(UNUSED(Param pm))
|
||||||
{
|
{
|
||||||
char **arr;
|
char **arr;
|
||||||
char buf[DIGBUFSIZE];
|
char buf[DIGBUFSIZE];
|
||||||
|
|
||||||
#ifdef HAVE_CLOCK_GETTIME
|
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
|
|
||||||
if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
|
zgettime(&now);
|
||||||
zwarn("%s: unable to retrieve time: %e", pm->node.nam, errno);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
arr = (char **)zhalloc(3 * sizeof(*arr));
|
arr = (char **)zhalloc(3 * sizeof(*arr));
|
||||||
sprintf(buf, "%ld", (long)now.tv_sec);
|
sprintf(buf, "%ld", (long)now.tv_sec);
|
||||||
arr[0] = dupstring(buf);
|
arr[0] = dupstring(buf);
|
||||||
sprintf(buf, "%ld", now.tv_nsec);
|
sprintf(buf, "%ld", (long)now.tv_nsec);
|
||||||
arr[1] = dupstring(buf);
|
arr[1] = dupstring(buf);
|
||||||
arr[2] = NULL;
|
arr[2] = NULL;
|
||||||
|
|
||||||
return arr;
|
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[] = {
|
static struct builtin bintab[] = {
|
||||||
|
|
|
@ -188,7 +188,7 @@ static char *timefmt;
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
static void
|
static void
|
||||||
stattimeprint(time_t tim, char *outbuf, int flags)
|
stattimeprint(time_t tim, long nsecs, char *outbuf, int flags)
|
||||||
{
|
{
|
||||||
if (flags & STF_RAW) {
|
if (flags & STF_RAW) {
|
||||||
sprintf(outbuf, "%ld", (unsigned long)tim);
|
sprintf(outbuf, "%ld", (unsigned long)tim);
|
||||||
|
@ -199,7 +199,7 @@ stattimeprint(time_t tim, char *outbuf, int flags)
|
||||||
char *oend = outbuf + strlen(outbuf);
|
char *oend = outbuf + strlen(outbuf);
|
||||||
/* Where the heck does "40" come from? */
|
/* Where the heck does "40" come from? */
|
||||||
int len = ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) :
|
int len = ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) :
|
||||||
localtime(&tim), 0L);
|
localtime(&tim), nsecs);
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
metafy(oend, len, META_NOALLOC);
|
metafy(oend, len, META_NOALLOC);
|
||||||
if (flags & STF_RAW)
|
if (flags & STF_RAW)
|
||||||
|
@ -291,15 +291,27 @@ statprint(struct stat *sbuf, char *outbuf, char *fname, int iwhich, int flags)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ST_ATIM:
|
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;
|
break;
|
||||||
|
|
||||||
case ST_MTIM:
|
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;
|
break;
|
||||||
|
|
||||||
case ST_CTIM:
|
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;
|
break;
|
||||||
|
|
||||||
case ST_BLKSIZE:
|
case ST_BLKSIZE:
|
||||||
|
|
33
Src/compat.c
33
Src/compat.c
|
@ -94,6 +94,39 @@ gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||||
#endif
|
#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 */
|
/* compute the difference between two calendar times */
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
|
|
|
@ -273,8 +273,7 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
|
||||||
char *ss, *hostnam;
|
char *ss, *hostnam;
|
||||||
int t0, arg, test, sep, j, numjobs, len;
|
int t0, arg, test, sep, j, numjobs, len;
|
||||||
struct tm *tm;
|
struct tm *tm;
|
||||||
struct timezone dummy_tz;
|
struct timespec ts;
|
||||||
struct timeval tv;
|
|
||||||
time_t timet;
|
time_t timet;
|
||||||
Nameddir nd;
|
Nameddir nd;
|
||||||
|
|
||||||
|
@ -664,8 +663,8 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
|
||||||
tmfmt = "%l:%M%p";
|
tmfmt = "%l:%M%p";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
gettimeofday(&tv, &dummy_tz);
|
zgettime(&ts);
|
||||||
tm = localtime(&tv.tv_sec);
|
tm = localtime(&ts.tv_sec);
|
||||||
/*
|
/*
|
||||||
* Hack because strftime won't say how
|
* Hack because strftime won't say how
|
||||||
* much space it actually needs. Try to add it
|
* 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) {
|
for(j = 0, t0 = strlen(tmfmt)*8; j < 3; j++, t0*=2) {
|
||||||
addbufspc(t0);
|
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)
|
>= 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
22
Src/utils.c
22
Src/utils.c
|
@ -3224,7 +3224,7 @@ ztrftimebuf(int *bufsizeptr, int decr)
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
mod_export int
|
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;
|
int hr12;
|
||||||
#ifdef HAVE_STRFTIME
|
#ifdef HAVE_STRFTIME
|
||||||
|
@ -3299,15 +3299,15 @@ morefmt:
|
||||||
case '.':
|
case '.':
|
||||||
if (ztrftimebuf(&bufsize, digs))
|
if (ztrftimebuf(&bufsize, digs))
|
||||||
return -1;
|
return -1;
|
||||||
if (digs > 6)
|
if (digs > 9)
|
||||||
digs = 6;
|
digs = 9;
|
||||||
if (digs < 6) {
|
if (digs < 9) {
|
||||||
int trunc;
|
int trunc;
|
||||||
for (trunc = 5 - digs; trunc; trunc--)
|
for (trunc = 8 - digs; trunc; trunc--)
|
||||||
usec /= 10;
|
nsec /= 10;
|
||||||
usec = (usec + 5) / 10;
|
nsec = (nsec + 8) / 10;
|
||||||
}
|
}
|
||||||
sprintf(buf, "%0*ld", digs, usec);
|
sprintf(buf, "%0*ld", digs, nsec);
|
||||||
buf += digs;
|
buf += digs;
|
||||||
break;
|
break;
|
||||||
case '\0':
|
case '\0':
|
||||||
|
@ -3369,6 +3369,12 @@ morefmt:
|
||||||
*buf++ = '0' + tm->tm_min / 10;
|
*buf++ = '0' + tm->tm_min / 10;
|
||||||
*buf++ = '0' + tm->tm_min % 10;
|
*buf++ = '0' + tm->tm_min % 10;
|
||||||
break;
|
break;
|
||||||
|
case 'N':
|
||||||
|
if (ztrftimebuf(&bufsize, 9))
|
||||||
|
return -1;
|
||||||
|
sprintf(buf, "%09ld", nsec);
|
||||||
|
buf += 9;
|
||||||
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
if (tm->tm_sec > 9 || !strip)
|
if (tm->tm_sec > 9 || !strip)
|
||||||
*buf++ = '0' + tm->tm_sec / 10;
|
*buf++ = '0' + tm->tm_sec / 10;
|
||||||
|
|
|
@ -250,6 +250,14 @@ struct timezone {
|
||||||
};
|
};
|
||||||
#endif
|
#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 */
|
/* 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)
|
#if !defined(HAVE_STRUCT_DIRENT_D_INO) && defined(HAVE_STRUCT_DIRENT_D_STAT)
|
||||||
# define d_ino d_stat.st_ino
|
# define d_ino d_stat.st_ino
|
||||||
|
|
|
@ -108,6 +108,14 @@
|
||||||
if (( $date2[7,8] != $date3[1,2] )); then
|
if (( $date2[7,8] != $date3[1,2] )); then
|
||||||
print "Years do not agree in $date2, $date3"
|
print "Years do not agree in $date2, $date3"
|
||||||
fi
|
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
|
0:Dates produced by prompt escapes
|
||||||
|
|
||||||
mkdir foo
|
mkdir foo
|
||||||
|
|
|
@ -1113,6 +1113,14 @@ zsh_TYPE_EXISTS([
|
||||||
#endif
|
#endif
|
||||||
], struct timezone)
|
], 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
|
dnl Check for utmp structures, for watch
|
||||||
zsh_TYPE_EXISTS([
|
zsh_TYPE_EXISTS([
|
||||||
#ifdef HAVE_SYS_TYPES_H
|
#ifdef HAVE_SYS_TYPES_H
|
||||||
|
|
Loading…
Reference in a new issue