summaryrefslogtreecommitdiffstats
path: root/tests/date
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 16:58:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 16:58:41 +0000
commite1908ae95dd4c9d19ee4dfabfc8bf8a7f85943fe (patch)
treef5cc731bedcac0fb7fe14d952e4581e749f8bb87 /tests/date
parentInitial commit. (diff)
downloadcoreutils-e1908ae95dd4c9d19ee4dfabfc8bf8a7f85943fe.tar.xz
coreutils-e1908ae95dd4c9d19ee4dfabfc8bf8a7f85943fe.zip
Adding upstream version 9.4.upstream/9.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/date')
-rwxr-xr-xtests/date/date-debug.sh311
-rwxr-xr-xtests/date/date-next-dow.pl78
-rwxr-xr-xtests/date/date-sec.sh49
-rwxr-xr-xtests/date/date-tz.sh26
-rwxr-xr-xtests/date/date.pl381
5 files changed, 845 insertions, 0 deletions
diff --git a/tests/date/date-debug.sh b/tests/date/date-debug.sh
new file mode 100755
index 0000000..f9e42f9
--- /dev/null
+++ b/tests/date/date-debug.sh
@@ -0,0 +1,311 @@
+#!/bin/sh
+# Test 'date --debug' option.
+
+# Copyright (C) 2016-2023 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 <https://www.gnu.org/licenses/>.
+
+. "${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'
+
+date --debug >/dev/null 2>d_t_fmt.err || fail=1
+d_t_fmt=$(sed -n "s/.*'\(.*\)'$/\1/p" < d_t_fmt.err) || framework_failure_
+test -n "$d_t_fmt" || fail=1
+
+##
+## Test 1: complex date string
+##
+in1='TZ="Asia/Tokyo" Sun, 90-12-11 + 3 days - 90 minutes'
+
+cat<<EOF>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: 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'
+date: warning: when adding relative days, it is recommended to specify noon
+date: after date adjustment (+0 years, +0 months, +3 days),
+date: new date/time = '(Y-M-D) 1990-12-14 00:00:00'
+date: '(Y-M-D) 1990-12-14 00:00:00' = 661100400 epoch-seconds
+date: after time adjustment (+0 hours, -90 minutes, +0 seconds, +0 ns),
+date: new time = 661095000 epoch-seconds
+date: timezone: TZ="Asia/Tokyo" environment value
+date: final: 661095000.000000000 (epoch-seconds)
+date: final: (Y-M-D) 1990-12-13 13:30:00 (UTC)
+date: final: (Y-M-D) 1990-12-13 22:30:00 (UTC+09)
+date: output format: '%a %b %e %T %z %Y'
+Thu Dec 13 07:30:00 -0600 1990
+EOF
+
+TZ=America/Belize date --debug -d "$in1" +'%a %b %e %T %z %Y' >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<<EOF>exp2
+date: parsed date part: (Y-M-D) 2006-04-02
+date: parsed time part: 02:30:00
+date: input timezone: 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'
+date: normalized time: '(Y-M-D) 2006-04-02 XX:XX:XX'
+date: --
+date: possible reasons:
+date: nonexistent 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-t 2>&1 || fail=1
+
+# The output line of "normalized time" can differ between systems
+# (e.g. glibc vs musl) and should not be checked.
+# See: https://lists.gnu.org/archive/html/coreutils/2019-05/msg00039.html
+sed '/normalized time:/s/ [0-9][0-9]:[0-9][0-9]:[0-9][0-9]/ XX:XX:XX/' \
+ out2-t > out2 || framework_failure_
+
+compare exp2 out2 || fail=1
+
+##
+## Test 3: timespec (input always UTC, output is TZ-dependent)
+##
+in3='@1'
+cat<<EOF>exp3
+date: parsed number of seconds part: number of seconds: 1
+date: input timezone: '@timespec' - always UTC
+date: timezone: TZ="America/Lima" environment value
+date: final: 1.000000000 (epoch-seconds)
+date: final: (Y-M-D) 1970-01-01 00:00:01 (UTC)
+date: final: (Y-M-D) 1969-12-31 19:00:01 (UTC-05)
+date: output format: '%a %b %e %T %z %Y'
+Wed Dec 31 19:00:01 -0500 1969
+EOF
+
+TZ=America/Lima date --debug -d "$in3" +'%a %b %e %T %z %Y' >out3 2>&1 || fail=1
+compare exp3 out3 || fail=1
+
+##
+## Parsing a lone number.
+## Fixed in gnulib v0.1-1099-gf2d4b5c
+## https://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=f2d4b5caa
+cat<<EOF>exp4
+date: parsed number part: (Y-M-D) 2013-01-01
+date: input timezone: TZ="UTC0" environment value or -u
+date: warning: using midnight as starting time: 00:00:00
+date: starting date/time: '(Y-M-D) 2013-01-01 00:00:00'
+date: '(Y-M-D) 2013-01-01 00:00:00' = 1356998400 epoch-seconds
+date: timezone: Universal Time
+date: final: 1356998400.000000000 (epoch-seconds)
+date: final: (Y-M-D) 2013-01-01 00:00:00 (UTC)
+date: final: (Y-M-D) 2013-01-01 00:00:00 (UTC+00)
+date: output format: '$d_t_fmt'
+Tue Jan 1 00:00:00 UTC 2013
+EOF
+
+date -u --debug -d '20130101' >out4 2>&1 || fail=1
+compare exp4 out4 || fail=1
+
+
+##
+## Parsing a relative number after a timezone string
+## Fixed in gnulib v0.1-1100-g5c438e8
+## https://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=5c438e8ce7d
+cat<<EOF>exp5
+date: parsed date part: (Y-M-D) 2013-10-30
+date: parsed time part: 00:00:00
+date: parsed relative part: -8 day(s)
+date: parsed zone part: UTC+00
+date: input timezone: parsed date/time string (+00)
+date: using specified time as starting value: '00:00:00'
+date: starting date/time: '(Y-M-D) 2013-10-30 00:00:00 TZ=+00'
+date: warning: when adding relative days, it is recommended to specify noon
+date: after date adjustment (+0 years, +0 months, -8 days),
+date: new date/time = '(Y-M-D) 2013-10-22 00:00:00 TZ=+00'
+date: '(Y-M-D) 2013-10-22 00:00:00 TZ=+00' = 1382400000 epoch-seconds
+date: timezone: Universal Time
+date: final: 1382400000.000000000 (epoch-seconds)
+date: final: (Y-M-D) 2013-10-22 00:00:00 (UTC)
+date: final: (Y-M-D) 2013-10-22 00:00:00 (UTC+00)
+date: output format: '%F'
+2013-10-22
+EOF
+
+in5='2013-10-30 00:00:00 UTC -8 days'
+date -u --debug +%F -d "$in5" >out5 2>&1 || fail=1
+compare exp5 out5 || fail=1
+
+##
+## Explicitly warn about unexpected day/month shifts.
+## added in gnulib v0.1-1101-gf14eff1
+## https://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=f14eff1b3cde2b
+TOOLONG='it is recommended to specify the 15th of the months'
+cat<<EOF>exp6
+date: parsed date part: (Y-M-D) 2016-10-31
+date: parsed relative part: -1 month(s)
+date: input timezone: TZ="UTC0" environment value or -u
+date: warning: using midnight as starting time: 00:00:00
+date: starting date/time: '(Y-M-D) 2016-10-31 00:00:00'
+date: warning: when adding relative months/years, $TOOLONG
+date: after date adjustment (+0 years, -1 months, +0 days),
+date: new date/time = '(Y-M-D) 2016-10-01 00:00:00'
+date: warning: month/year adjustment resulted in shifted dates:
+date: adjusted Y M D: 2016 09 31
+date: normalized Y M D: 2016 10 01
+date: '(Y-M-D) 2016-10-01 00:00:00' = 1475280000 epoch-seconds
+date: timezone: Universal Time
+date: final: 1475280000.000000000 (epoch-seconds)
+date: final: (Y-M-D) 2016-10-01 00:00:00 (UTC)
+date: final: (Y-M-D) 2016-10-01 00:00:00 (UTC+00)
+date: output format: '$d_t_fmt'
+Sat Oct 1 00:00:00 UTC 2016
+EOF
+
+date -u --debug -d '2016-10-31 - 1 month' >out6 2>&1 || fail=1
+compare exp6 out6 || fail=1
+
+
+##
+## Explicitly warn about crossing DST boundaries.
+## added in gnulib v0.1-1102-g30a55dd
+## https://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=30a55dd72dad2
+TOOLONG2='it is recommended to specify the 15th of the months'
+cat<<EOF>exp7
+date: parsed date part: (Y-M-D) 2016-06-01
+date: parsed local_zone part: isdst=1
+date: parsed relative part: +6 month(s)
+date: input timezone: TZ="America/New_York" environment value, dst
+date: warning: using midnight as starting time: 00:00:00
+date: starting date/time: '(Y-M-D) 2016-06-01 00:00:00'
+date: warning: when adding relative months/years, $TOOLONG2
+date: after date adjustment (+0 years, +6 months, +0 days),
+date: new date/time = '(Y-M-D) 2016-11-30 23:00:00'
+date: warning: daylight saving time changed after date adjustment
+date: warning: month/year adjustment resulted in shifted dates:
+date: adjusted Y M D: 2016 12 01
+date: normalized Y M D: 2016 11 30
+date: '(Y-M-D) 2016-11-30 23:00:00' = 1480564800 epoch-seconds
+date: timezone: TZ="America/New_York" environment value
+date: final: 1480564800.000000000 (epoch-seconds)
+date: final: (Y-M-D) 2016-12-01 04:00:00 (UTC)
+date: final: (Y-M-D) 2016-11-30 23:00:00 (UTC-05)
+date: output format: '%F'
+2016-11-30
+EOF
+
+in7='2016-06-01 EDT + 6 months'
+TZ=America/New_York date --debug -d "$in7" +%F >out7 2>&1 || fail=1
+compare exp7 out7 || fail=1
+
+
+## fix local timezone debug messages.
+## fixed in git v0.1-1103-gc56e7fb
+## https://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=c56e7fbb032
+
+cat<<EOF>exp8_1
+date: parsed date part: (Y-M-D) 2011-12-11
+date: parsed local_zone part: isdst=0
+date: input timezone: TZ="Europe/Helsinki" environment value
+date: warning: using midnight as starting time: 00:00:00
+date: starting date/time: '(Y-M-D) 2011-12-11 00:00:00'
+date: '(Y-M-D) 2011-12-11 00:00:00' = 1323554400 epoch-seconds
+date: timezone: TZ="Europe/Helsinki" environment value
+date: final: 1323554400.000000000 (epoch-seconds)
+date: final: (Y-M-D) 2011-12-10 22:00:00 (UTC)
+date: final: (Y-M-D) 2011-12-11 00:00:00 (UTC+02)
+date: output format: '$d_t_fmt'
+Sun Dec 11 00:00:00 EET 2011
+EOF
+
+TZ=Europe/Helsinki date --debug -d '2011-12-11 EET' >out8_1 2>&1 || fail=1
+compare exp8_1 out8_1 || fail=1
+
+cat<<EOF>exp8_2
+date: parsed date part: (Y-M-D) 2011-06-11
+date: parsed local_zone part: isdst=1
+date: input timezone: TZ="Europe/Helsinki" environment value, dst
+date: warning: using midnight as starting time: 00:00:00
+date: starting date/time: '(Y-M-D) 2011-06-11 00:00:00'
+date: '(Y-M-D) 2011-06-11 00:00:00' = 1307739600 epoch-seconds
+date: timezone: TZ="Europe/Helsinki" environment value
+date: final: 1307739600.000000000 (epoch-seconds)
+date: final: (Y-M-D) 2011-06-10 21:00:00 (UTC)
+date: final: (Y-M-D) 2011-06-11 00:00:00 (UTC+03)
+date: output format: '$d_t_fmt'
+Sat Jun 11 00:00:00 EEST 2011
+EOF
+
+TZ=Europe/Helsinki date --debug -d '2011-06-11 EEST' >out8_2 2>&1 || fail=1
+compare exp8_2 out8_2 || fail=1
+
+
+
+## fix debug message on lone year number (The "2011" part).
+## fixed in gnulib v0.1-1104-g15b8f30
+## https://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=15b8f3046a25
+##
+## NOTE:
+## When the date 'Apr 11' is parsed, the year part will be the
+## current year. The expected output thus depends on the year
+## the test is being run. We'll use sed to change it to XXXX.
+cat<<EOF>exp9
+date: parsed date part: (Y-M-D) XXXX-04-11
+date: parsed time part: 22:59:00
+date: parsed number part: year: 2011
+date: input timezone: TZ="UTC0" environment value or -u
+date: using specified time as starting value: '22:59:00'
+date: starting date/time: '(Y-M-D) 2011-04-11 22:59:00'
+date: '(Y-M-D) 2011-04-11 22:59:00' = 1302562740 epoch-seconds
+date: timezone: Universal Time
+date: final: 1302562740.000000000 (epoch-seconds)
+date: final: (Y-M-D) 2011-04-11 22:59:00 (UTC)
+date: final: (Y-M-D) 2011-04-11 22:59:00 (UTC+00)
+date: output format: '$d_t_fmt'
+Mon Apr 11 22:59:00 UTC 2011
+EOF
+
+date -u --debug -d 'Apr 11 22:59:00 2011' >out9_t 2>&1 || fail=1
+sed '1s/(Y-M-D) [0-9][0-9][0-9][0-9]-/(Y-M-D) XXXX-/' out9_t > out9 \
+ || framework_failure_
+compare exp9 out9 || fail=1
+
+
+# Diagnose discarded -d arguments
+echo 'date: only using last of multiple -d options' > exp10 \
+ || framework_failure_
+cat exp9 >> exp10 || framework_failure_
+date -u --debug -d 'discard' -d 'Apr 11 22:59:00 2011' > out10_t 2>&1 || fail=1
+sed '2s/(Y-M-D) [0-9][0-9][0-9][0-9]-/(Y-M-D) XXXX-/' out10_t >> out10 \
+ || framework_failure_
+compare exp10 out10 || fail=1
+
+
+Exit $fail
diff --git a/tests/date/date-next-dow.pl b/tests/date/date-next-dow.pl
new file mode 100755
index 0000000..ec414c7
--- /dev/null
+++ b/tests/date/date-next-dow.pl
@@ -0,0 +1,78 @@
+#!/usr/bin/perl
+# Test "date".
+
+# Copyright (C) 2005-2023 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 <https://www.gnu.org/licenses/>.
+
+use strict;
+use POSIX qw(strftime);
+
+(my $ME = $0) =~ s|.*/||;
+
+# Turn off localization of executable's output.
+@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3;
+
+# Export TZ=UTC0 so that zone-dependent strings match.
+$ENV{TZ} = 'UTC0';
+
+my $now = time;
+my @d = localtime ($now);
+my @d_week = localtime ($now + 7 * 24 * 3600);
+my $wday = $d[6];
+my $wday_str = qw(sun mon tue wed thu fri sat)[$wday];
+
+my @Tests =
+ (
+ # test-name, [option, option, ...] {OUT=>"expected-output"}
+ #
+
+ # Running "date -d mon +%a" on a Monday must print Mon.
+ ['dow', "-d $wday_str +%a", {OUT => ucfirst $wday_str}],
+ # It had better be the same date, too.
+ ['dow2', "-d $wday_str +%Y-%m-%d", {OUT => strftime("%Y-%m-%d", @d)}],
+
+ ['next-dow', "-d 'next $wday_str' +%Y-%m-%d",
+ {OUT => strftime("%Y-%m-%d", @d_week)}],
+ );
+
+# Append "\n" to each OUT=> RHS if the expected exit value is either
+# zero or not specified (defaults to zero).
+foreach my $t (@Tests)
+ {
+ my $exit_val;
+ foreach my $e (@$t)
+ {
+ ref $e && ref $e eq 'HASH' && defined $e->{EXIT}
+ and $exit_val = $e->{EXIT};
+ }
+ foreach my $e (@$t)
+ {
+ ref $e && ref $e eq 'HASH' && defined $e->{OUT} && ! $exit_val
+ and $e->{OUT} .= "\n";
+ }
+ }
+
+my $save_temps = $ENV{DEBUG};
+my $verbose = $ENV{VERBOSE};
+
+my $prog = 'date';
+my $fail = run_tests ($ME, $prog, \@Tests, $save_temps, $verbose);
+
+# Skip the test if the starting and stopping day numbers differ.
+my @d_post = localtime (time);
+$d_post[7] == $d[7]
+ or CuSkip::skip "$ME: test straddled a day boundary; skipped";
+
+exit $fail;
diff --git a/tests/date/date-sec.sh b/tests/date/date-sec.sh
new file mode 100755
index 0000000..f18590e
--- /dev/null
+++ b/tests/date/date-sec.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+# Ensure that a command like
+# date --date="21:04 +0100" +%S' always prints '00'.
+# Before coreutils-5.2.1, it would print the seconds from the current time.
+
+# Copyright (C) 2004-2023 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 <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ date
+
+
+
+# It would be easier simply to sleep for two seconds between two runs
+# of $(date --date="21:04 +0100" +%S) and ensure that both outputs
+# are '00', but I prefer not to sleep unconditionally. 'make check'
+# takes long enough as it is.
+
+n=0
+# See if the current number of seconds is '00' or just before.
+s=$(date +%S)
+case "$s" in
+ 58) n=3;;
+ 59) n=2;;
+ 00) n=1;;
+esac
+
+# If necessary, wait for the system clock to pass the minute mark.
+test $n = 0 || sleep $n
+
+s=$(date --date="21:04 +0100" +%S)
+case "$s" in
+ 00) ;;
+ *) fail=1;;
+esac
+
+Exit $fail
diff --git a/tests/date/date-tz.sh b/tests/date/date-tz.sh
new file mode 100755
index 0000000..f6cf071
--- /dev/null
+++ b/tests/date/date-tz.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+# Verify TZ processing.
+
+# Copyright (C) 2017-2023 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 <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ date
+
+# coreutils-8.27 would overwrite the heap with large TZ values
+tz_long=$(printf '%2000s' | tr ' ' a)
+date -d "TZ=\"${tz_long}0\" 2017" || fail=1
+
+Exit $fail
diff --git a/tests/date/date.pl b/tests/date/date.pl
new file mode 100755
index 0000000..d18f8b1
--- /dev/null
+++ b/tests/date/date.pl
@@ -0,0 +1,381 @@
+#!/usr/bin/perl
+# Test "date".
+
+# Copyright (C) 2005-2023 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 <https://www.gnu.org/licenses/>.
+
+use strict;
+
+(my $ME = $0) =~ s|.*/||;
+
+# Turn off localization of executable's output.
+@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3;
+
+# Export TZ=UTC0 so that zone-dependent strings match.
+$ENV{TZ} = 'UTC0';
+
+my $t0 = '08:17:48';
+my $d0 = '1997-01-19';
+my $d1 = "$d0 $t0 +0";
+my $dT = "${d0}T$t0+0"; # ISO 8601 with "T" separator
+
+my $ts = '08:17:49'; # next second
+my $tm = '08:18:48'; # next minute
+my $th = '09:17:48'; # next hour
+
+my $dd = '1997-01-20'; # next day
+my $dw = '1997-01-26'; # next week
+my $dm = '1997-02-19'; # next month
+my $dy = '1998-01-19'; # next month
+
+my $fmt = "'+%Y-%m-%d %T'";
+
+my @Tests =
+ (
+ # test-name, [option, option, ...] {OUT=>"expected-output"}
+ #
+ ['1', "-d '$d1' +'%% %a %A %b %B'", {OUT=>"% Sun Sunday Jan January"}],
+
+ # [Actually, skip it on *all* systems. -- this Perl code is run at
+ # distribution-build-time, not at configure/test time. ]
+
+ # Skip the test of %c on SunOS4 systems. Such systems would fail this
+ # test because their underlying strftime doesn't handle the %c format
+ # properly. GNU strftime must rely on the underlying host library
+ # function to get locale-dependent behavior, as strftime is the only
+ # portable interface to that behavior.
+ # ['2', "-d '$d1' +'%c'", {OUT=>"Sun Jan 19 $t0 1997"}],
+
+ ['3', "-d '$d1' +'%d_%D_%e_%h_%H'", {OUT=>"19_01/19/97_19_Jan_08"}],
+ ['3T',"-d '$dT' +'%d_%D_%e_%h_%H'", {OUT=>"19_01/19/97_19_Jan_08"}],
+
+ ['4', "-d '$d1' +'%I_%j_%k_%l_%m'", {OUT=>"08_019_ 8_ 8_01"}],
+ ['5', "-d '$d1' +'%M_%n_%p_%r'", {OUT=>"17_\n_AM_$t0 AM"}],
+ ['6', "-d '$d1' +'%s_%S_%t_%T'", {OUT=>"853661868_48_\t_$t0"}],
+ ['7', "-d '$d1' +'%U_%V_%w_%W'", {OUT=>"03_03_0_02"}],
+ ['8', "-d '$d1' +'%x_%X_%y_%Y'", {OUT=>"01/19/97_${t0}_97_1997"}],
+ ['9', "-d '$d1' +'%z'", {OUT=>"+0000"}],
+
+ ['leap-1', "--date '02/29/1996 1 year' +%Y-%m-%d", {OUT=>"1997-03-01"}],
+
+ ['U95-1', "--date '1995-1-1' +%U", {OUT=>"01"}],
+ ['U95-2', "--date '1995-1-7' +%U", {OUT=>"01"}],
+ ['U95-3', "--date '1995-1-8' +%U", {OUT=>"02"}],
+
+ ['U92-1', "--date '1992-1-1' +%U", {OUT=>"00"}],
+ ['U92-2', "--date '1992-1-4' +%U", {OUT=>"00"}],
+ ['U92-3', "--date '1992-1-5' +%U", {OUT=>"01"}],
+
+ ['V92-1', "--date '1992-1-1' +%V", {OUT=>"01"}],
+ ['V92-2', "--date '1992-1-5' +%V", {OUT=>"01"}],
+ ['V92-3', "--date '1992-1-6' +%V", {OUT=>"02"}],
+
+ ['W92-1', "--date '1992-1-1' +%W", {OUT=>"00"}],
+ ['W92-2', "--date '1992-1-5' +%W", {OUT=>"00"}],
+ ['W92-3', "--date '1992-1-6' +%W", {OUT=>"01"}],
+
+ ['q-1', "--date '2016-1-1' +%q", {OUT=>"1"}],
+ ['q-2', "--date '2016-4-1' +%q", {OUT=>"2"}],
+ ['q-3', "--date '2016-7-1' +%q", {OUT=>"3"}],
+ ['q-4', "--date '2016-10-1' +%q", {OUT=>"4"}],
+
+ ['millen-1', "--date '1998-1-1 3 years' +%Y", {OUT=>"2001"}],
+
+ ['rel-0', "-d '$d1 now' '+%Y-%m-%d %T'", {OUT=>"$d0 $t0"}],
+
+ ['rel-1a', "-d '$d1 yesterday' $fmt", {OUT=>"1997-01-18 $t0"}],
+ ['rel-1b', "-d '$d1 tomorrow' $fmt", {OUT=>"1997-01-20 $t0"}],
+
+ ['rel-2a', "-d '$d1 6 years ago' $fmt", {OUT=>"1991-01-19 $t0"}],
+ ['rel-2b', "-d '$d1 7 months ago' $fmt", {OUT=>"1996-06-19 $t0"}],
+ ['rel-2c', "-d '$d1 8 weeks ago' $fmt", {OUT=>"1996-11-24 $t0"}],
+ ['rel-2d', "-d '$d1 1 day ago' $fmt", {OUT=>"1997-01-18 $t0"}],
+ ['rel-2e', "-d '$d1 2 hours ago' $fmt", {OUT=>"$d0 06:17:48"}],
+ ['rel-2f', "-d '$d1 3 minutes ago' $fmt", {OUT=>"$d0 08:14:48"}],
+ ['rel-2g', "-d '$d1 4 seconds ago' $fmt", {OUT=>"$d0 08:17:44"}],
+
+ ['rel-3a', "-d '$d1 4 seconds ago' $fmt", {OUT=>"$d0 08:17:44"}],
+
+ # This has always worked, ...
+ ['rel-1day', "-d '20050101 1 day' +%F", {OUT=>"2005-01-02"}],
+ # ...but up to coreutils-6.9, this was rejected due to the "+".
+ ['rel-plus1', "-d '20050101 +1 day' +%F", {OUT=>"2005-01-02"}],
+
+ ['next-s', "-d '$d1 next second' '+%Y-%m-%d %T'", {OUT=>"$d0 $ts"}],
+ ['next-m', "-d '$d1 next minute' '+%Y-%m-%d %T'", {OUT=>"$d0 $tm"}],
+ ['next-h', "-d '$d1 next hour' '+%Y-%m-%d %T'", {OUT=>"$d0 $th"}],
+ ['next-d', "-d '$d1 next day' '+%Y-%m-%d %T'", {OUT=>"$dd $t0"}],
+ ['next-w', "-d '$d1 next week' '+%Y-%m-%d %T'", {OUT=>"$dw $t0"}],
+ ['next-mo', "-d '$d1 next month' '+%Y-%m-%d %T'", {OUT=>"$dm $t0"}],
+ ['next-y', "-d '$d1 next year' '+%Y-%m-%d %T'", {OUT=>"$dy $t0"}],
+
+ ['utc-0', "-u -d '08/01/97 6:00' '+%D,%H:%M'", {OUT=>"08/01/97,06:00"},
+ {ENV => 'TZ=UTC+4'}],
+
+ ['utc-0a', "-u -d '08/01/97 6:00 UTC +4 hours' '+%D,%H:%M'",
+ {OUT=>"08/01/97,10:00"}],
+ # Make sure --file=FILE works with -u.
+ ['utc-1', "-u --file=f '+%Y-%m-%d %T'",
+ {AUX=>{f=>"$d0 $t0\n$d0 $t0"}},
+ {OUT=>"$d0 $t0\n$d0 $t0"},
+ {ENV => 'TZ=UTC+1'}],
+
+ ['utc-1a', "-u --file=f '+%Y-%m-%d %T'",
+ {AUX=>{f=>"$d0 $t0 UTC +1 hour\n$d0 $t0 UTC +1 hour"}},
+ {OUT=>"$d0 $th\n$d0 $th"}],
+
+ # From the examples in the documentation.
+ ['date2sec-0', "-d '1970-01-01 00:00:01' +%s", {OUT=>"7201"},
+ {ENV => 'TZ=UTC+2'}],
+
+ # Same as above, but don't rely on TZ in environment.
+ ['date2sec-0a', "-d '1970-01-01 00:00:01 UTC +2 hours' +%s",
+ {OUT=>"7201"}],
+
+ ['date2sec-1', "-d 2000-01-01 +%s", {OUT=>"946684800"}],
+ ['sec2date-0', "-d '1970-01-01 UTC 946684800 sec' +'%Y-%m-%d %T %z'",
+ {OUT=>"2000-01-01 00:00:00 +0000"}],
+
+ ['this-m', "-d '$d0 $t0 this minute' $fmt", {OUT=>"$d0 $t0"}],
+ ['this-h', "-d '$d0 $t0 this hour' $fmt", {OUT=>"$d0 $t0"}],
+ ['this-w', "-d '$d0 $t0 this week' $fmt", {OUT=>"$d0 $t0"}],
+ ['this-mo', "-d '$d0 $t0 this month' $fmt", {OUT=>"$d0 $t0"}],
+ ['this-y', "-d '$d0 $t0 this year' $fmt", {OUT=>"$d0 $t0"}],
+
+ ['risks-1', "-d 'Nov 10 1996' $fmt", {OUT=>"1996-11-10 00:00:00"}],
+
+ # This one would pass if TZ (with any, or even no, value) were in
+ # the environment.
+ ['regress-1', "-u -d '1996-11-10 0:00:00 +0' $fmt",
+ {OUT=>"1996-11-10 00:00:00"},
+ {ENV =>'LANG=C'}],
+
+
+ ['datevtime-1', "-d 000909 $fmt", {OUT=>"2000-09-09 00:00:00"}],
+
+ # test for RFC-822 conformance
+ ['rfc822-1', "-R -d '$d1'", {OUT=>"Sun, 19 Jan 1997 08:17:48 +0000"},
+ # Solaris 5.9's /bin/sh emits this diagnostic to stderr
+ # if you don't have support for the named locale.
+ {ERR_SUBST => q!s/^couldn't set locale correctly\n//!},
+ {ENV => 'LC_ALL=de_DE TZ=UTC0'}],
+
+ # Relative seconds, with time. fixed in 2.0j
+ ['relative-1', "--utc -d '1970-01-01 00:00:00 UTC +961062237 sec' $fmt",
+ {OUT=>"2000-06-15 09:43:57"}],
+
+ # Relative seconds, no time.
+ ['relative-2', "--utc -d '1970-01-01 UTC +961062237 sec' $fmt",
+ {OUT=>"2000-06-15 09:43:57"},
+ {ENV => 'TZ=UTC+1'}],
+
+ # Relative days, no time, across time zones.
+ ['relative-3', "-I -d '2006-04-23 21 days ago'", {OUT=>"2006-04-02"},
+ {ENV=>'TZ=PST8PDT,M4.1.0,M10.5.0'}],
+
+ # This would infloop (or appear to) prior to coreutils-4.5.5,
+ # due to a bug in strftime.c.
+ ['wide-fmt', "-d '1999-06-01'", '+%3004Y', {OUT=>'0' x 3000 . "1999"}],
+
+ # Ensure that we can parse MONTHNAME-DAY-YEAR.
+ ['moname-d-y', '--iso -d May-23-2003', {OUT=>"2003-05-23"}],
+ ['moname-d-y-r', '--rfc-3339=date -d May-23-2003', {OUT=>"2003-05-23"}],
+
+ ['epoch', '--iso=sec -d @31536000',
+ {OUT=>"1971-01-01T00:00:00+00:00"}],
+ ['epoch-r', '--rfc-3339=sec -d @31536000',
+ {OUT=>"1971-01-01 00:00:00+00:00"}],
+
+ ['ns-10', '--iso=ns', '-d "1969-12-31 13:00:00.00000001-1100"',
+ {OUT=>"1970-01-01T00:00:00,000000010+00:00"}],
+ ['ns-10-r', '--rfc-3339=ns', '-d "1969-12-31 13:00:00.00000001-1100"',
+ {OUT=>"1970-01-01 00:00:00.000000010+00:00"}],
+
+ ['ns-max32', '--iso=ns', '-d "2038-01-19 03:14:07.999999999"',
+ {OUT=>"2038-01-19T03:14:07,999999999+00:00"}],
+ ['ns-max32-r', '--rfc-3339=ns', '-d "2038-01-19 03:14:07.999999999"',
+ {OUT=>"2038-01-19 03:14:07.999999999+00:00"}],
+
+ ['tz-1', '+%:::z', {OUT=>"-12:34:56"}, {ENV=>'TZ=XXX12:34:56'}],
+
+ ['tz-2', '+%:::z', {OUT=>"+12:34:56"}, {ENV=>'TZ=XXX-12:34:56'}],
+
+ ['tz-3', '+%::z', {OUT=>"+01:02:03"}, {ENV=>'TZ=XXX-1:02:03'}],
+
+ ['tz-4', '+%:::z', {OUT=>"+12"}, {ENV=>'TZ=XXX-12'}],
+
+ ['tz-5', '+%:z', {OUT=>"-00:01"}, {ENV=>'TZ=XXX0:01'}],
+
+ # Accept %:z with a field width before the ':'.
+ ['tz-5w','+%8:z', {OUT=>"-0000:01"}, {ENV=>'TZ=XXX0:01'}],
+ # Don't recognize %:z with a field width between the ':' and the 'z'.
+ ['tz-5wf', '+%:8z', {OUT=>"%:8z"}, {ENV=>'TZ=XXX0:01'}],
+
+ # Test alphabetic timezone abbrv
+ ['tz-6', '+%Z', {OUT=>"UTC"}],
+ ['tz-7', '+%Z', {OUT=>"JST"}, {ENV=>'TZ=JST-9'}],
+
+ ['ns-relative',
+ '--iso=ns',
+ "-d'1970-01-01 00:00:00.1234567 UTC +961062237.987654321 sec'",
+ {OUT=>"2000-06-15T09:43:58,111111021+00:00"}],
+ ['ns-relativer', '--rfc-3339=ns',
+ "-d'1970-01-01 00:00:00.1234567 UTC +961062237.987654321 sec'",
+ {OUT=>"2000-06-15 09:43:58.111111021+00:00"}],
+
+ # Since coreutils/lib/getdate.y revision 1.96 (post-coreutils-5.3.0),
+ # a command like the following would mistakenly exit nonzero with an
+ # 'invalid date ...' diagnostic, but when run in a time zone for
+ # which daylight savings time is in effect for the starting date.
+ # Unfortunately (for ease of testing), if you set TZ at all, this
+ # failure is not triggered, hence the removal of TZ from the environment.
+ ['cross-dst', "-d'2005-03-27 +1 day'", '+%Y', {OUT=>"2005"},
+ {ENV_DEL => 'TZ'},
+ ],
+
+ ['empty-fmt', '+', {OUT=>""}],
+
+ ['neg-secs', '-d @-22 +%05s', {OUT=>"-0022"}],
+ ['neg-secs2', '-d @-22 +%_5s', {OUT=>" -22"}],
+
+ # FIXME: Ensure date doesn't print uninitialized data
+ # for an out-of-range date. This test is currently
+ # disabled as various systems have different limits
+ # for localtime(), and we can't use perl for example
+ # to determine those limits as it doesn't always call
+ # down to the system localtime() as it has configure
+ # time checks and settings itself for these limits.
+ #['uninit-64', "-d \@72057594037927935",
+ # {OUT=>''},
+ # # Use ERR_SUBST to get around fact that the diagnostic
+ # # you get on a system with 32-bit time_t is not the same as
+ # # the one you get for a system where it's 64 bits wide:
+ # # - date: time 72057594037927935 is out of range
+ # # + date: invalid date '@72057594037927935'
+ # {ERR_SUBST => 's/.*//'},
+ # {ERR => "\n"},
+ # {EXIT => 1},
+ #],
+
+ ['fill-1', '-d 1999-12-08 +%_3d', {OUT=>' 8'}],
+ ['fill-2', '-d 1999-12-08 +%03d', {OUT=>'008'}],
+
+ # Test the combination of the to-upper-case modifier (^) and a conversion
+ # specifier that expands to a string containing lower case characters.
+ ['subfmt-up1', '-d "1999-12-08 7:30" "+%^c"',
+ # Solaris 5.9 prints 'WED DEC 08 07:30:00 1999', while
+ # most others print 'WED DEC 8 07:30:00 1999'.
+ {OUT_SUBST => 's/ [ 0]8.*//'},
+ {OUT=>'WED DEC'}],
+
+ ['invalid-high-bit-set', "-d '\xb0'",
+ {ERR => "date: invalid date '\\260'\n"},
+ {EXIT => 1},
+ ],
+
+ # From coreutils-5.3.0 to 8.22 inclusive
+ # this would either infinite loop or crash
+ ['invalid-TZ-crash', "-d 'TZ=\"\"\"'",
+ {ERR => "date: invalid date 'TZ=\"\"\"'\n"},
+ {EXIT => 1},
+ ],
+
+ # https://bugs.debian.org/851934#10
+ ['cross-TZ-mishandled', "-d 'TZ=\"EST5\" 1970-01-01 00:00'",
+ {ENV => 'TZ=PST8'},
+ {OUT => 'Wed Dec 31 21:00:00 PST 1969'},
+ ],
+
+ # https://bugs.gnu.org/34608
+ ['date-century-plus', '-d @0 +.%+4C.', {OUT => '.+019.'}],
+
+ # https://bugs.gnu.org/50115
+ ['date-epoch-minus-1', '-u -d "1970-12-31T23:59:59+00:00 - 1 year"',
+ {OUT => 'Wed Dec 31 23:59:59 UTC 1969'}],
+
+ # Military time zones, new behavior (since 8.32)
+ # https://lists.gnu.org/r/bug-gnulib/2019-08/msg00005.html
+ ['mtz1', '-u -d "09:00B" +%T', {OUT => '07:00:00'}],
+ ['mtz2', '-u -d "09:00L" +%T', {OUT => '22:00:00'}],
+ ['mtz3', '-u -d "09:00N" +%T', {OUT => '10:00:00'}],
+ ['mtz4', '-u -d "09:00T" +%T', {OUT => '16:00:00'}],
+ ['mtz5', '-u -d "09:00X" +%T', {OUT => '20:00:00'}],
+ ['mtz6', '-u -d "09:00Z" +%T', {OUT => '09:00:00'}],
+
+ # test with %%-N
+ ['pct-pct', '+%%-N', {OUT => '%-N'}],
+ );
+
+# Repeat the cross-dst test, using Jan 1, 2005 and every interval from 1..364.
+foreach my $i (1..364)
+ {
+ push @Tests, ["cross-dst$i",
+ "-d'2005-01-01 +$i day'", '+%Y', {OUT=>"2005"},
+ {ENV_DEL => 'TZ'},
+ ];
+ }
+
+# Append "\n" to each OUT=> RHS if the expected exit value is either
+# zero or not specified (defaults to zero).
+foreach my $t (@Tests)
+ {
+ my $exit_val;
+ foreach my $e (@$t)
+ {
+ ref $e && ref $e eq 'HASH' && defined $e->{EXIT}
+ and $exit_val = $e->{EXIT};
+ }
+ foreach my $e (@$t)
+ {
+ ref $e && ref $e eq 'HASH' && defined $e->{OUT} && ! $exit_val
+ and $e->{OUT} .= "\n";
+ }
+ }
+
+# 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};
+
+my $prog = 'date';
+my $fail = run_tests ($ME, $prog, \@Tests, $save_temps, $verbose);
+exit $fail;