1#!/bin/sh
2# Test 'date --debug' option.
3
4# Copyright (C) 2016-2023 Free Software Foundation, Inc.
5
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15
16# You should have received a copy of the GNU General Public License
17# along with this program.  If not, see <https://www.gnu.org/licenses/>.
18
19. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
20print_ver_ date
21
22export LC_ALL=C
23
24## Ensure timezones are supported.
25## (NOTE: America/Belize timezone does not change on DST)
26test "$(TZ=America/Belize date +%z)" = '-0600' \
27    || skip_ 'Timezones database not found'
28
29date --debug >/dev/null 2>d_t_fmt.err || fail=1
30d_t_fmt=$(sed -n "s/.*'\(.*\)'$/\1/p" < d_t_fmt.err) || framework_failure_
31test -n "$d_t_fmt" || fail=1
32
33##
34## Test 1: complex date string
35##
36in1='TZ="Asia/Tokyo" Sun, 90-12-11 + 3 days - 90 minutes'
37
38cat<<EOF>exp1
39date: parsed day part: Sun (day ordinal=0 number=0)
40date: parsed date part: (Y-M-D) 0090-12-11
41date: parsed relative part: +3 day(s)
42date: parsed relative part: +3 day(s) -90 minutes
43date: input timezone: TZ="Asia/Tokyo" in date string
44date: warning: adjusting year value 90 to 1990
45date: warning: using midnight as starting time: 00:00:00
46date: warning: day (Sun) ignored when explicit dates are given
47date: starting date/time: '(Y-M-D) 1990-12-11 00:00:00'
48date: warning: when adding relative days, it is recommended to specify noon
49date: after date adjustment (+0 years, +0 months, +3 days),
50date:     new date/time = '(Y-M-D) 1990-12-14 00:00:00'
51date: '(Y-M-D) 1990-12-14 00:00:00' = 661100400 epoch-seconds
52date: after time adjustment (+0 hours, -90 minutes, +0 seconds, +0 ns),
53date:     new time = 661095000 epoch-seconds
54date: timezone: TZ="Asia/Tokyo" environment value
55date: final: 661095000.000000000 (epoch-seconds)
56date: final: (Y-M-D) 1990-12-13 13:30:00 (UTC)
57date: final: (Y-M-D) 1990-12-13 22:30:00 (UTC+09)
58date: output format: '%a %b %e %T %z %Y'
59Thu Dec 13 07:30:00 -0600 1990
60EOF
61
62TZ=America/Belize date --debug -d "$in1" +'%a %b %e %T %z %Y' >out1 2>&1 ||
63  fail=1
64
65compare exp1 out1 || fail=1
66
67##
68## Test 2: Invalid date from Coreutils' FAQ
69##         (with explicit timezone added)
70in2='TZ="America/Edmonton" 2006-04-02 02:30:00'
71cat<<EOF>exp2
72date: parsed date part: (Y-M-D) 2006-04-02
73date: parsed time part: 02:30:00
74date: input timezone: TZ="America/Edmonton" in date string
75date: using specified time as starting value: '02:30:00'
76date: error: invalid date/time value:
77date:     user provided time: '(Y-M-D) 2006-04-02 02:30:00'
78date:        normalized time: '(Y-M-D) 2006-04-02 XX:XX:XX'
79date:                                             --
80date:      possible reasons:
81date:        nonexistent due to daylight-saving time;
82date:        numeric values overflow;
83date:        missing timezone
84date: invalid date 'TZ="America/Edmonton" 2006-04-02 02:30:00'
85EOF
86
87# date should return 1 (error) for invalid date
88returns_ 1 date --debug -d "$in2" >out2-t 2>&1 || fail=1
89
90# The output line of "normalized time" can differ between systems
91# (e.g. glibc vs musl) and should not be checked.
92# See: https://lists.gnu.org/archive/html/coreutils/2019-05/msg00039.html
93sed '/normalized time:/s/ [0-9][0-9]:[0-9][0-9]:[0-9][0-9]/ XX:XX:XX/' \
94    out2-t > out2 || framework_failure_
95
96compare exp2 out2 || fail=1
97
98##
99## Test 3: timespec (input always UTC, output is TZ-dependent)
100##
101in3='@1'
102cat<<EOF>exp3
103date: parsed number of seconds part: number of seconds: 1
104date: input timezone: '@timespec' - always UTC
105date: timezone: TZ="America/Lima" environment value
106date: final: 1.000000000 (epoch-seconds)
107date: final: (Y-M-D) 1970-01-01 00:00:01 (UTC)
108date: final: (Y-M-D) 1969-12-31 19:00:01 (UTC-05)
109date: output format: '%a %b %e %T %z %Y'
110Wed Dec 31 19:00:01 -0500 1969
111EOF
112
113TZ=America/Lima date --debug -d "$in3" +'%a %b %e %T %z %Y' >out3 2>&1 || fail=1
114compare exp3 out3 || fail=1
115
116##
117## Parsing a lone number.
118## Fixed in gnulib v0.1-1099-gf2d4b5c
119## https://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=f2d4b5caa
120cat<<EOF>exp4
121date: parsed number part: (Y-M-D) 2013-01-01
122date: input timezone: TZ="UTC0" environment value or -u
123date: warning: using midnight as starting time: 00:00:00
124date: starting date/time: '(Y-M-D) 2013-01-01 00:00:00'
125date: '(Y-M-D) 2013-01-01 00:00:00' = 1356998400 epoch-seconds
126date: timezone: Universal Time
127date: final: 1356998400.000000000 (epoch-seconds)
128date: final: (Y-M-D) 2013-01-01 00:00:00 (UTC)
129date: final: (Y-M-D) 2013-01-01 00:00:00 (UTC+00)
130date: output format: '$d_t_fmt'
131Tue Jan  1 00:00:00 UTC 2013
132EOF
133
134date -u --debug -d '20130101' >out4 2>&1 || fail=1
135compare exp4 out4 || fail=1
136
137
138##
139## Parsing a relative number after a timezone string
140## Fixed in gnulib v0.1-1100-g5c438e8
141## https://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=5c438e8ce7d
142cat<<EOF>exp5
143date: parsed date part: (Y-M-D) 2013-10-30
144date: parsed time part: 00:00:00
145date: parsed relative part: -8 day(s)
146date: parsed zone part: UTC+00
147date: input timezone: parsed date/time string (+00)
148date: using specified time as starting value: '00:00:00'
149date: starting date/time: '(Y-M-D) 2013-10-30 00:00:00 TZ=+00'
150date: warning: when adding relative days, it is recommended to specify noon
151date: after date adjustment (+0 years, +0 months, -8 days),
152date:     new date/time = '(Y-M-D) 2013-10-22 00:00:00 TZ=+00'
153date: '(Y-M-D) 2013-10-22 00:00:00 TZ=+00' = 1382400000 epoch-seconds
154date: timezone: Universal Time
155date: final: 1382400000.000000000 (epoch-seconds)
156date: final: (Y-M-D) 2013-10-22 00:00:00 (UTC)
157date: final: (Y-M-D) 2013-10-22 00:00:00 (UTC+00)
158date: output format: '%F'
1592013-10-22
160EOF
161
162in5='2013-10-30 00:00:00 UTC -8 days'
163date -u --debug +%F -d "$in5" >out5 2>&1 || fail=1
164compare exp5 out5 || fail=1
165
166##
167## Explicitly warn about unexpected day/month shifts.
168## added in gnulib v0.1-1101-gf14eff1
169## https://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=f14eff1b3cde2b
170TOOLONG='it is recommended to specify the 15th of the months'
171cat<<EOF>exp6
172date: parsed date part: (Y-M-D) 2016-10-31
173date: parsed relative part: -1 month(s)
174date: input timezone: TZ="UTC0" environment value or -u
175date: warning: using midnight as starting time: 00:00:00
176date: starting date/time: '(Y-M-D) 2016-10-31 00:00:00'
177date: warning: when adding relative months/years, $TOOLONG
178date: after date adjustment (+0 years, -1 months, +0 days),
179date:     new date/time = '(Y-M-D) 2016-10-01 00:00:00'
180date: warning: month/year adjustment resulted in shifted dates:
181date:      adjusted Y M D: 2016 09 31
182date:    normalized Y M D: 2016 10 01
183date: '(Y-M-D) 2016-10-01 00:00:00' = 1475280000 epoch-seconds
184date: timezone: Universal Time
185date: final: 1475280000.000000000 (epoch-seconds)
186date: final: (Y-M-D) 2016-10-01 00:00:00 (UTC)
187date: final: (Y-M-D) 2016-10-01 00:00:00 (UTC+00)
188date: output format: '$d_t_fmt'
189Sat Oct  1 00:00:00 UTC 2016
190EOF
191
192date -u --debug -d '2016-10-31 - 1 month' >out6 2>&1 || fail=1
193compare exp6 out6 || fail=1
194
195
196##
197## Explicitly warn about crossing DST boundaries.
198## added in gnulib v0.1-1102-g30a55dd
199## https://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=30a55dd72dad2
200TOOLONG2='it is recommended to specify the 15th of the months'
201cat<<EOF>exp7
202date: parsed date part: (Y-M-D) 2016-06-01
203date: parsed local_zone part: isdst=1
204date: parsed relative part: +6 month(s)
205date: input timezone: TZ="America/New_York" environment value, dst
206date: warning: using midnight as starting time: 00:00:00
207date: starting date/time: '(Y-M-D) 2016-06-01 00:00:00'
208date: warning: when adding relative months/years, $TOOLONG2
209date: after date adjustment (+0 years, +6 months, +0 days),
210date:     new date/time = '(Y-M-D) 2016-11-30 23:00:00'
211date: warning: daylight saving time changed after date adjustment
212date: warning: month/year adjustment resulted in shifted dates:
213date:      adjusted Y M D: 2016 12 01
214date:    normalized Y M D: 2016 11 30
215date: '(Y-M-D) 2016-11-30 23:00:00' = 1480564800 epoch-seconds
216date: timezone: TZ="America/New_York" environment value
217date: final: 1480564800.000000000 (epoch-seconds)
218date: final: (Y-M-D) 2016-12-01 04:00:00 (UTC)
219date: final: (Y-M-D) 2016-11-30 23:00:00 (UTC-05)
220date: output format: '%F'
2212016-11-30
222EOF
223
224in7='2016-06-01 EDT + 6 months'
225TZ=America/New_York date --debug -d "$in7" +%F >out7 2>&1 || fail=1
226compare exp7 out7 || fail=1
227
228
229## fix local timezone debug messages.
230## fixed in git v0.1-1103-gc56e7fb
231## https://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=c56e7fbb032
232
233cat<<EOF>exp8_1
234date: parsed date part: (Y-M-D) 2011-12-11
235date: parsed local_zone part: isdst=0
236date: input timezone: TZ="Europe/Helsinki" environment value
237date: warning: using midnight as starting time: 00:00:00
238date: starting date/time: '(Y-M-D) 2011-12-11 00:00:00'
239date: '(Y-M-D) 2011-12-11 00:00:00' = 1323554400 epoch-seconds
240date: timezone: TZ="Europe/Helsinki" environment value
241date: final: 1323554400.000000000 (epoch-seconds)
242date: final: (Y-M-D) 2011-12-10 22:00:00 (UTC)
243date: final: (Y-M-D) 2011-12-11 00:00:00 (UTC+02)
244date: output format: '$d_t_fmt'
245Sun Dec 11 00:00:00 EET 2011
246EOF
247
248TZ=Europe/Helsinki date --debug -d '2011-12-11 EET' >out8_1 2>&1 || fail=1
249compare exp8_1 out8_1 || fail=1
250
251cat<<EOF>exp8_2
252date: parsed date part: (Y-M-D) 2011-06-11
253date: parsed local_zone part: isdst=1
254date: input timezone: TZ="Europe/Helsinki" environment value, dst
255date: warning: using midnight as starting time: 00:00:00
256date: starting date/time: '(Y-M-D) 2011-06-11 00:00:00'
257date: '(Y-M-D) 2011-06-11 00:00:00' = 1307739600 epoch-seconds
258date: timezone: TZ="Europe/Helsinki" environment value
259date: final: 1307739600.000000000 (epoch-seconds)
260date: final: (Y-M-D) 2011-06-10 21:00:00 (UTC)
261date: final: (Y-M-D) 2011-06-11 00:00:00 (UTC+03)
262date: output format: '$d_t_fmt'
263Sat Jun 11 00:00:00 EEST 2011
264EOF
265
266TZ=Europe/Helsinki date --debug -d '2011-06-11 EEST' >out8_2 2>&1 || fail=1
267compare exp8_2 out8_2 || fail=1
268
269
270
271## fix debug message on lone year number (The "2011" part).
272## fixed in gnulib v0.1-1104-g15b8f30
273## https://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=15b8f3046a25
274##
275## NOTE:
276## When the date 'Apr 11' is parsed, the year part will be the
277## current year. The expected output thus depends on the year
278## the test is being run. We'll use sed to change it to XXXX.
279cat<<EOF>exp9
280date: parsed date part: (Y-M-D) XXXX-04-11
281date: parsed time part: 22:59:00
282date: parsed number part: year: 2011
283date: input timezone: TZ="UTC0" environment value or -u
284date: using specified time as starting value: '22:59:00'
285date: starting date/time: '(Y-M-D) 2011-04-11 22:59:00'
286date: '(Y-M-D) 2011-04-11 22:59:00' = 1302562740 epoch-seconds
287date: timezone: Universal Time
288date: final: 1302562740.000000000 (epoch-seconds)
289date: final: (Y-M-D) 2011-04-11 22:59:00 (UTC)
290date: final: (Y-M-D) 2011-04-11 22:59:00 (UTC+00)
291date: output format: '$d_t_fmt'
292Mon Apr 11 22:59:00 UTC 2011
293EOF
294
295date -u --debug -d 'Apr 11 22:59:00 2011' >out9_t 2>&1 || fail=1
296sed '1s/(Y-M-D) [0-9][0-9][0-9][0-9]-/(Y-M-D) XXXX-/' out9_t > out9 \
297    || framework_failure_
298compare exp9 out9 || fail=1
299
300
301# Diagnose discarded -d arguments
302echo 'date: only using last of multiple -d options' > exp10 \
303    || framework_failure_
304cat exp9 >> exp10 || framework_failure_
305date -u --debug -d 'discard' -d 'Apr 11 22:59:00 2011' > out10_t 2>&1 || fail=1
306sed '2s/(Y-M-D) [0-9][0-9][0-9][0-9]-/(Y-M-D) XXXX-/' out10_t >> out10 \
307    || framework_failure_
308compare exp10 out10 || fail=1
309
310
311Exit $fail
312