From c6389a1340cdc2a74dbbba5db13df009ada3c04c Mon Sep 17 00:00:00 2001 From: Assaf Gordon Date: Thu, 8 Sep 2016 01:00:23 -0400 Subject: date: add '--debug' option Usage example: date --debug -d 'now + 3 days' Print parsing and debugging information to stderr when using date's -d/--date option. See: http://lists.gnu.org/archive/html/coreutils/2016-01/msg00076.html * src/date.c (main): Add '--debug' option, enable debugging in gnulib's parse-datetime.y module. * tests/misc/date.pl: Repeat tests with '--debug' enable, ensure no regression. * tests/misc/date-debug.sh: Test output of '--debug' option. * tests/local.mk: Add above test. * NEWS: Mention new option. * doc/coreutils.texi: Likewise. --- NEWS | 5 +++ doc/coreutils.texi | 12 +++++- src/date.c | 21 ++++++++-- tests/local.mk | 1 + tests/misc/date-debug.sh | 103 +++++++++++++++++++++++++++++++++++++++++++++++ tests/misc/date.pl | 29 +++++++++++++ 6 files changed, 166 insertions(+), 5 deletions(-) create mode 100755 tests/misc/date-debug.sh diff --git a/NEWS b/NEWS index e99c88b96..580c5f4d7 100644 --- a/NEWS +++ b/NEWS @@ -71,6 +71,11 @@ GNU coreutils NEWS -*- outline -*- tail -f uses polling for "prl_fs" and "smb2", inotify for "m1fs", and attempts inotify for "wslfs". +** New Features + + date now accepts the --debug option, to annotate the parsed date string, + display timezone information, and warn about potential misuse. + * Noteworthy changes in release 8.25 (2016-01-20) [stable] diff --git a/doc/coreutils.texi b/doc/coreutils.texi index cc5e53cf5..95419b44e 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -521,8 +521,8 @@ Include the version number, machine architecture, input files, and any other information needed to reproduce the bug: your input, what you expected, what you got, and why it is wrong. -If you have a problem with @command{sort}, try running @samp{sort ---debug}, as it can can often help find and fix problems without +If you have a problem with @command{sort} or @command{date}, try using the +@option{--debug} option, as it can can often help find and fix problems without having to wait for an answer to a bug report. If the debug output does not suffice to fix the problem on your own, please compress and attach it to the rest of your bug report. @@ -15392,6 +15392,14 @@ date -d "$(LC_TIME=C date)" @end example @xref{Date input formats}. +@item --debug +@opindex --debug +@cindex debugging date strings +@cindex date strings, debugging +@cindex arbitrary date strings, debugging +annotate the parsed date, display the effective time zone, and warn about +potential misuse. + @item -f @var{datefile} @itemx --file=@var{datefile} @opindex -f diff --git a/src/date.c b/src/date.c index e73196b96..03968784f 100644 --- a/src/date.c +++ b/src/date.c @@ -78,7 +78,8 @@ static char const rfc_2822_format[] = "%a, %d %b %Y %H:%M:%S %z"; non-character as a pseudo short option, starting with CHAR_MAX + 1. */ enum { - RFC_3339_OPTION = CHAR_MAX + 1 + RFC_3339_OPTION = CHAR_MAX + 1, + DEBUG_DATE_PARSING }; static char const short_options[] = "d:f:I::r:Rs:u"; @@ -86,6 +87,7 @@ static char const short_options[] = "d:f:I::r:Rs:u"; static struct option const long_options[] = { {"date", required_argument, NULL, 'd'}, + {"debug", no_argument, NULL, DEBUG_DATE_PARSING}, {"file", required_argument, NULL, 'f'}, {"iso-8601", optional_argument, NULL, 'I'}, {"reference", required_argument, NULL, 'r'}, @@ -101,6 +103,9 @@ static struct option const long_options[] = {NULL, 0, NULL, 0} }; +/* flags for parse_datetime2 */ +static unsigned int parse_datetime_flags; + #if LOCALTIME_CACHE # define TZSET tzset () #else @@ -133,6 +138,12 @@ Display the current time in the given FORMAT, or set the system date.\n\ fputs (_("\ -d, --date=STRING display time described by STRING, not 'now'\n\ +"), stdout); + fputs (_("\ + --debug annotate the parsed date,\n\ + and warn about questionable usage to stderr\n\ +"), stdout); + fputs (_("\ -f, --file=DATEFILE like --date; once for each line of DATEFILE\n\ "), stdout); fputs (_("\ @@ -306,7 +317,7 @@ batch_convert (const char *input_filename, const char *format, timezone_t tz) break; } - if (! parse_datetime (&when, line, NULL)) + if (! parse_datetime2 (&when, line, NULL, parse_datetime_flags)) { if (line[line_length - 1] == '\n') line[line_length - 1] = '\0'; @@ -360,6 +371,9 @@ main (int argc, char **argv) case 'd': datestr = optarg; break; + case DEBUG_DATE_PARSING: + parse_datetime_flags |= PARSE_DATETIME_DEBUG; + break; case 'f': batch_file = optarg; break; @@ -527,7 +541,8 @@ main (int argc, char **argv) { if (set_datestr) datestr = set_datestr; - valid_date = parse_datetime (&when, datestr, NULL); + valid_date = parse_datetime2 (&when, datestr, NULL, + parse_datetime_flags); } } diff --git a/tests/local.mk b/tests/local.mk index 889142af4..86de37603 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -279,6 +279,7 @@ all_tests = \ tests/misc/csplit-heap.sh \ tests/misc/csplit-io-err.sh \ tests/misc/csplit-suppress-matched.pl \ + tests/misc/date-debug.sh \ tests/misc/date-sec.sh \ tests/misc/dircolors.pl \ tests/misc/dirname.pl \ diff --git a/tests/misc/date-debug.sh b/tests/misc/date-debug.sh new file mode 100755 index 000000000..94ef917a2 --- /dev/null +++ b/tests/misc/date-debug.sh @@ -0,0 +1,103 @@ +#!/bin/sh +# Test 'date --debug' option. + +# Copyright (C) 2016 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ date + +export LC_ALL=C + +## Ensure timezones are supported. +## (NOTE: America/Belize timezone does not change on DST) +test "$(TZ=America/Belize date +%z)" = '-0600' \ + || skip_ 'Timezones database not found' + + +## +## Test 1: complex date string +## +in1='TZ="Asia/Tokyo" Sun, 90-12-11 + 3 days - 90 minutes' + +cat<exp1 +date: parsed day part: Sun (day ordinal=0 number=0) +date: parsed date part: (Y-M-D) 0090-12-11 +date: parsed relative part: +3 day(s) +date: parsed relative part: +3 day(s) -90 minutes +date: input timezone: +09:00 (set from TZ="Asia/Tokyo" in date string) +date: warning: adjusting year value 90 to 1990 +date: warning: using midnight as starting time: 00:00:00 +date: warning: day (Sun) ignored when explicit dates are given +date: starting date/time: '(Y-M-D) 1990-12-11 00:00:00 TZ=+09:00' +date: warning: when adding relative days, it is recommended to specify 12:00pm +date: after date adjustment (+0 years, +0 months, +3 days), +date: new date/time = '(Y-M-D) 1990-12-14 00:00:00 TZ=+09:00' +date: '(Y-M-D) 1990-12-14 00:00:00 TZ=+09:00' = 661100400 epoch-seconds +date: after time adjustment (+0 hours, -90 minutes, +0 seconds, +0 ns), +date: new time = 661095000 epoch-seconds +date: output timezone: -06:00 (set from TZ="America/Belize" environment value) +date: final: 661095000.000000000 (epoch-seconds) +date: final: (Y-M-D) 1990-12-13 13:30:00 (UTC0) +date: final: (Y-M-D) 1990-12-13 07:30:00 (output timezone TZ=-06:00) +Thu Dec 13 07:30:00 CST 1990 +EOF + +TZ=America/Belize date --debug -d "$in1" >out1 2>&1 || fail=1 + +compare exp1 out1 || fail=1 + +## +## Test 2: Invalid date from Coreutils' FAQ +## (with explicit timezone added) +in2='TZ="America/Edmonton" 2006-04-02 02:30:00' +cat<exp2 +date: parsed date part: (Y-M-D) 2006-04-02 +date: parsed time part: 02:30:00 +date: input timezone: -07:00 (set from TZ="America/Edmonton" in date string) +date: using specified time as starting value: '02:30:00' +date: error: invalid date/time value: +date: user provided time: '(Y-M-D) 2006-04-02 02:30:00 TZ=-07:00' +date: normalized time: '(Y-M-D) 2006-04-02 03:30:00 TZ=-07:00' +date: -- +date: possible reasons: +date: non-existing due to daylight-saving time; +date: numeric values overflow; +date: missing timezone +date: invalid date 'TZ="America/Edmonton" 2006-04-02 02:30:00' +EOF + +# date should return 1 (error) for invalid date +returns_ 1 date --debug -d "$in2" >out2 2>&1 || fail=1 +compare exp2 out2 || fail=1 + +## +## Test 3: timespec (input always UTC, output is TZ-dependent) +## +in3='@1' +cat<exp3 +date: parsed number of seconds part: number of seconds: 1 +date: input timezone: +00:00 (set from '@timespec' - always UTC0) +date: output timezone: -05:00 (set from TZ="America/Lima" environment value) +date: final: 1.000000000 (epoch-seconds) +date: final: (Y-M-D) 1970-01-01 00:00:01 (UTC0) +date: final: (Y-M-D) 1969-12-31 19:00:01 (output timezone TZ=-05:00) +Wed Dec 31 19:00:01 PET 1969 +EOF + +TZ=America/Lima date --debug -d "$in3" >out3 2>&1 || fail=1 +compare exp3 out3 || fail=1 + +Exit $fail diff --git a/tests/misc/date.pl b/tests/misc/date.pl index 3454dcc5b..4449a48e4 100755 --- a/tests/misc/date.pl +++ b/tests/misc/date.pl @@ -314,6 +314,35 @@ foreach my $t (@Tests) } } +# Repeat all tests with --debug option, ensure it does not cause any regression +my @debug_tests; +foreach my $t (@Tests) + { + # Skip tests with EXIT!=0 or ERR_SUBST part + # (as '--debug' requires its own ERR_SUBST). + my $exit_val; + my $have_err_subst; + foreach my $e (@$t) + { + next unless ref $e && ref $e eq 'HASH'; + $exit_val = $e->{EXIT} if defined $e->{EXIT}; + $have_err_subst = 1 if defined $e->{ERR_SUBST}; + } + next if $exit_val || $have_err_subst; + + # Duplicate the test, add '--debug' argument + my @newt = @$t; + $newt[0] = 'dbg_' . $newt[0]; + $newt[1] = '--debug ' . $newt[1]; + + # Discard all debug printouts before comparing output + push @newt, {ERR_SUBST => q!s/^date: .*\n//m!}; + + push @debug_tests, \@newt; + } +push @Tests, @debug_tests; + + my $save_temps = $ENV{DEBUG}; my $verbose = $ENV{VERBOSE}; -- cgit v1.2.3-54-g00ecf