From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=DKIMWL_WL_MED,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from primenet.com.au (ns1.primenet.com.au [203.24.36.2]) by inbox.vuxu.org (OpenSMTPD) with ESMTP id fb0e780a for ; Wed, 7 Nov 2018 22:19:36 +0000 (UTC) Received: (qmail 25018 invoked by alias); 7 Nov 2018 22:19:22 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: List-Unsubscribe: X-Seq: 43800 Received: (qmail 2972 invoked by uid 1010); 7 Nov 2018 22:19:22 -0000 X-Qmail-Scanner-Diagnostics: from mail-io1-f46.google.com by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.99.2/21882. spamassassin: 3.4.1. Clear:RC:0(209.85.166.46):SA:0(-2.0/5.0):. Processed in 2.22697 secs); 07 Nov 2018 22:19:22 -0000 X-Envelope-From: dana@dana.is X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dana-is.20150623.gappssmtp.com; s=20150623; h=from:content-transfer-encoding:mime-version:subject:message-id:date :to; bh=w/2iPfX7siAxrPlFpx8HAO2vp9QHf45oOwHNxANiAD4=; b=PXjIOjD05OUkY3CxPL4RQHOlOjeIL8sAPOd1R0yiroNUpolxyW2o/KonUCSIJbFWSJ kg0YJMoPDV2CrbZiVCtoiBHjESHyT7Dla+F3DFN1Y0Dcje1yyMUs9FRZk7OmnfhgyMEQ w56/NPKAOLWSt9MUeNRQsx/TxfpNWuktmB/9c+F14LDajjRNxyf5wzP8wyDw4lgBe9B6 LAEdfFdEkJoKr3PIKpOf93qJ4O/LhbOtMqDA7AuhuF58kbCVoOlj8Bv6bmwBnV6QY9n6 fk7GELKG8vrMjYpuDMjsqx82dDArXeP0WuZq3zmGc1KYmLUHvtDp4kccxFuxH6pOfF5I WV1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:content-transfer-encoding:mime-version :subject:message-id:date:to; bh=w/2iPfX7siAxrPlFpx8HAO2vp9QHf45oOwHNxANiAD4=; b=udXE8cdXToZL2nT+XJZdRK0Snpp6POmoII0VQHEUvxJhs2QnWA9BsyMFqWQWLQshdv 9rAqOxWTAC9wguS0XmpfhgHkjme9efezrpQbIztY9U+prtzQ93sydjEg9s0yfxreKBpe EW5TXlsYY4llptsnDd8TptDKlpJ6Zsn2xiloE9SMTLuXmY5qw/mT/hx1V2BoSJgsdKrV quhGBQ2efjYisNHo5tXWhF37AQh3DkpnpmA0vnMB0fCEkW0CnmnBqNl6H/iGI87c+E5F ZvBZAWcgWcMPdjUMG+zQxIe9x2tmGbFGG0FmL+vDfudoUU+VDUqR174K8zGpw6b1kfFf BfDA== X-Gm-Message-State: AGRZ1gKtTdGVlbfbbG2e+FKyeNKVbZmsf976xuwZzgpJ5PqJ7C9k275H OUmLXi5NowkI4STbJKpwMrZqy3aqoEp77w== X-Google-Smtp-Source: AJdET5cE8mhyJYQhx67DqvKp8Oi2xVvfOeCak5G4oq0hYSndQvAfccBTfIHPKn+nGdDlkqeAQYlFIQ== X-Received: by 2002:a5e:c902:: with SMTP id z2-v6mr1686380iol.72.1541629155898; Wed, 07 Nov 2018 14:19:15 -0800 (PST) From: dana Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: quoted-printable Mime-Version: 1.0 (Mac OS X Mail 11.5 \(3445.9.1\)) Subject: [PATCH] Add nanosecond support to strftime (zsh/datetime) Message-Id: Date: Wed, 7 Nov 2018 16:19:14 -0600 To: Zsh workers X-Mailer: Apple Mail (2.3445.9.1) 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=3D( $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 =3D NULL, *scalar =3D NULL, *buffer; - time_t secs; - struct tm *t; + struct tm *tm; + struct timespec ts; =20 if (OPT_ISSET(ops,'s')) { scalar =3D 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 =3D 0; - secs =3D (time_t)strtoul(argv[1], &endptr, 10); - if (errno !=3D 0) { - zwarnnam(nam, "%s: %e", argv[1], errno); - return 1; - } else if (*endptr !=3D '\0') { - zwarnnam(nam, "%s: invalid decimal number", argv[1]); - return 1; } =20 - t =3D localtime(&secs); - if (!t) { - zwarnnam(nam, "%s: unable to convert to time", argv[1]); - return 1; + if (!argv[1]) { + zgettime(&ts); + tm =3D localtime(&ts.tv_sec); + } else { + errno =3D 0; + + ts.tv_sec =3D (time_t)strtoul(argv[1], &endptr, 10); + if (errno !=3D 0) { + zwarnnam(nam, "%s: %e", argv[1], errno); + return 1; + } else if (*argv[1] =3D=3D '\0' || *endptr !=3D '\0') { + zwarnnam(nam, "%s: invalid decimal number", argv[1]); + return 1; + } + + tm =3D localtime(&ts.tv_sec); + if (!tm) { + zwarnnam(nam, "%s: unable to convert to time", argv[1]); + return 1; + } + + ts.tv_nsec =3D 0L; + if (argv[2]) { + ts.tv_nsec =3D (long)zstrtol(argv[2], &endptr, 10); + if (errno !=3D 0) { + zwarnnam(nam, "%s: %e", argv[2], errno); + return 1; + } else if (*argv[2] =3D=3D '\0' || *endptr !=3D '\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 =3D strlen(argv[0]) * 8; buffer =3D zalloc(bufsize); =20 len =3D 0; for (x=3D0; x < 4; x++) { - if ((len =3D ztrftime(buffer, bufsize, argv[0], t, 0L)) >=3D 0 = || x=3D=3D3) + if ((len =3D ztrftime(buffer, bufsize, argv[0], tm, = ts.tv_nsec)) >=3D 0 || + x=3D=3D3) break; buffer =3D zrealloc(buffer, bufsize *=3D 2); } @@ -207,7 +235,7 @@ getcurrenttime(UNUSED(Param pm)) } =20 static struct builtin bintab[] =3D { - BUILTIN("strftime", 0, bin_strftime, 2, 2, 0, "qrs:", = NULL), + BUILTIN("strftime", 0, bin_strftime, 1, 3, 0, "qrs:", = NULL), }; =20 static const struct gsu_integer epochseconds_gsu =3D 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) =3D=3D (%|)@ || $? !=3D 0 ]] 0:bad format specifier + +# This test may fail at 23:59:59.xxx on New Year's Eve :/ + [[ "$( strftime '%Y' )" =3D=3D "$( 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