OSDN Git Service

Implement date %N, loosely inspired by Elliott Hughes's patch.
authorRob Landley <rob@landley.net>
Fri, 19 May 2017 17:35:36 +0000 (12:35 -0500)
committerRob Landley <rob@landley.net>
Fri, 19 May 2017 17:35:36 +0000 (12:35 -0500)
I didn't implement %37N's ability to insert zeroes, so removed those
two tests. If you really need it, I can add the divide loop back.

tests/date.test
toys/posix/date.c

index d59d7fc..be80d45 100644 (file)
@@ -21,3 +21,14 @@ testing "-d 111014312015.30" "TZ=UTC date -d 111014312015.30 2>&1" "Sun Nov 10 1
 # Accidentally given a Unix time, we should trivially reject that.
 testing "Unix time missing @" "TZ=UTC date 1438053157 2>/dev/null || echo no" \
   "no\n" "" ""
+
+# Test the %N extension to srtftime(3) format strings.
+testing "%N" "touch -d 2012-01-23T12:34:56.123456789 f && date -r f +%Y%m%d-%H%M%S.%N" "20120123-123456.123456789\n" "" ""
+testing "%1N" "touch -d 2012-01-23T12:34:56.123456789 f && date -r f +%Y%m%d-%H%M%S.%1N" "20120123-123456.1\n" "" ""
+testing "%2N" "touch -d 2012-01-23T12:34:56.123456789 f && date -r f +%Y%m%d-%H%M%S.%2N" "20120123-123456.12\n" "" ""
+testing "%8N" "touch -d 2012-01-23T12:34:56.123456789 f && date -r f +%Y%m%d-%H%M%S.%8N" "20120123-123456.12345678\n" "" ""
+testing "%9N" "touch -d 2012-01-23T12:34:56.123456789 f && date -r f +%Y%m%d-%H%M%S.%9N" "20120123-123456.123456789\n" "" ""
+testing "%%N" "touch -d 2012-01-23T12:34:56.123456789 f && date -r f +%Y%m%d-%H%M%S.%%N" "20120123-123456.%N\n" "" ""
+testing "trailing %" "touch -d 2012-01-23T12:34:56.123456789 f && date -r f +%Y%m%d-%H%M%S.%" "20120123-123456.%\n" "" ""
+testing "just %" "touch -d 2012-01-23T12:34:56.123456789 f && date -r f +%" "%\n" "" ""
+rm -f f
index e77ff3a..56eb02b 100644 (file)
@@ -26,7 +26,7 @@ config DATE
     -r Use modification time of FILE instead of current date
     -u Use UTC instead of current timezone
 
-    +FORMAT specifies display format string using these escapes:
+    +FORMAT specifies display format string using strftime(3) syntax:
 
     %% literal %             %n newline              %t tab
     %S seconds (00-60)       %M minute (00-59)       %m month (01-12)
@@ -35,6 +35,7 @@ config DATE
     %a short weekday name    %A weekday name         %u day of week (1-7, 1=mon)
     %b short month name      %B month name           %Z timezone name
     %j day of year (001-366) %d day of month (01-31) %e day of month ( 1-31)
+    %N nanosec (output only)
 
     %U Week of year (0-53 start sunday)   %W Week of year (0-53 start monday)
     %V Week of year (1-53 start monday, week < 4 days not part of this year) 
@@ -123,12 +124,41 @@ static int parse_default(char *str, struct tm *tm)
   return *str;
 }
 
-void check_range(int a, int low, int high)
+static void check_range(int a, int low, int high)
 {
   if (a<low) error_exit("%d<%d", a, low);
   if (a>high) error_exit("%d>%d", a, high);
 }
 
+// Print strftime plus %N escape(s). note: modifies fmt for %N
+static void puts_time(char *fmt, struct tm *tm)
+{
+  char *s, *snap;
+  long width = width;
+
+  for (s = fmt;;s++) {
+
+    // Find next %N or end
+    if (*(snap = s) == '%') {
+      width = isdigit(*++s) ? *(s++)-'0' : 9;
+      if (*s && *s != 'N') continue;
+    } else if (*s) continue;
+
+    // Don't modify input string if no %N (default format is constant string).
+    if (*s) *snap = 0;
+    if (!strftime(toybuf, sizeof(toybuf)-10, fmt, tm))
+      perror_exit("bad format '%s'", fmt);
+    if (*s) {
+      snap = toybuf+strlen(toybuf);
+      sprintf(snap, "%09u", TT.nano);
+      snap[width] = 0;
+    }
+    fputs(toybuf, stdout);
+    if (!*s || !*(fmt = s+1)) break;
+  }
+  xputc('\n');
+}
+
 void date_main(void)
 {
   char *setdate = *toys.optargs, *format_string = "%a %b %e %H:%M:%S %Z %Y";
@@ -143,16 +173,16 @@ void date_main(void)
       if (!s || *s) goto bad_showdate;
     } else if (parse_default(TT.showdate, &tm)) goto bad_showdate;
   } else {
-    time_t now;
+    struct timespec ts;
+    struct stat st;
 
     if (TT.file) {
-      struct stat st;
-
       xstat(TT.file, &st);
-      now = st.st_mtim.tv_sec;
-    } else now = time(0);
+      ts = st.st_mtim;
+    } else clock_gettime(CLOCK_REALTIME, &ts);
 
-    ((toys.optflags & FLAG_u) ? gmtime_r : localtime_r)(&now, &tm);
+    ((toys.optflags & FLAG_u) ? gmtime_r : localtime_r)(&ts.tv_sec, &tm);
+    TT.nano = ts.tv_nsec;
   }
 
   // Fall through if no arguments
@@ -194,10 +224,7 @@ void date_main(void)
     if (settimeofday(&tv, NULL) < 0) perror_msg("cannot set date");
   }
 
-  if (!strftime(toybuf, sizeof(toybuf), format_string, &tm))
-    perror_exit("bad format '%s'", format_string);
-  puts(toybuf);
-
+  puts_time(format_string, &tm);
   return;
 
 bad_showdate: