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:
parent
eada7e1138
commit
394f3a47e4
11 changed files with 116 additions and 64 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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:
|
||||
|
|
33
Src/compat.c
33
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 */
|
||||
|
||||
/**/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
22
Src/utils.c
22
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue