2009-08-31 06:30:16 +04:00
|
|
|
#!/bin/sh
|
|
|
|
|
|
|
|
test_description='test date parsing and printing'
|
2022-02-16 11:14:05 +03:00
|
|
|
|
|
|
|
TEST_PASSES_SANITIZE_LEAK=true
|
2009-08-31 06:30:16 +04:00
|
|
|
. ./test-lib.sh
|
|
|
|
|
|
|
|
# arbitrary reference time: 2009-08-30 19:20:00
|
2019-01-29 06:50:15 +03:00
|
|
|
GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
|
2009-08-31 06:30:16 +04:00
|
|
|
|
2016-06-21 00:10:29 +03:00
|
|
|
check_relative() {
|
2019-01-29 06:50:15 +03:00
|
|
|
t=$(($GIT_TEST_DATE_NOW - $1))
|
2009-08-31 06:30:16 +04:00
|
|
|
echo "$t -> $2" >expect
|
|
|
|
test_expect_${3:-success} "relative date ($2)" "
|
2018-03-24 10:44:36 +03:00
|
|
|
test-tool date relative $t >actual &&
|
2021-02-11 04:53:53 +03:00
|
|
|
test_cmp expect actual
|
2009-08-31 06:30:16 +04:00
|
|
|
"
|
|
|
|
}
|
|
|
|
|
2016-06-21 00:10:29 +03:00
|
|
|
check_relative 5 '5 seconds ago'
|
|
|
|
check_relative 300 '5 minutes ago'
|
|
|
|
check_relative 18000 '5 hours ago'
|
|
|
|
check_relative 432000 '5 days ago'
|
|
|
|
check_relative 1728000 '3 weeks ago'
|
|
|
|
check_relative 13000000 '5 months ago'
|
|
|
|
check_relative 37500000 '1 year, 2 months ago'
|
|
|
|
check_relative 55188000 '1 year, 9 months ago'
|
|
|
|
check_relative 630000000 '20 years ago'
|
|
|
|
check_relative 31449600 '12 months ago'
|
|
|
|
check_relative 62985600 '2 years ago'
|
2009-08-31 06:30:16 +04:00
|
|
|
|
2016-06-21 00:11:59 +03:00
|
|
|
check_show () {
|
|
|
|
format=$1
|
|
|
|
time=$2
|
|
|
|
expect=$3
|
2017-06-15 16:51:22 +03:00
|
|
|
prereqs=$4
|
|
|
|
zone=$5
|
|
|
|
test_expect_success $prereqs "show date ($format:$time)" '
|
2016-06-21 00:11:59 +03:00
|
|
|
echo "$time -> $expect" >expect &&
|
2018-03-24 10:44:36 +03:00
|
|
|
TZ=${zone:-$TZ} test-tool date show:"$format" "$time" >actual &&
|
2016-06-21 00:11:59 +03:00
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
}
|
|
|
|
|
|
|
|
# arbitrary but sensible time for examples
|
|
|
|
TIME='1466000000 +0200'
|
|
|
|
check_show iso8601 "$TIME" '2016-06-15 16:13:20 +0200'
|
|
|
|
check_show iso8601-strict "$TIME" '2016-06-15T16:13:20+02:00'
|
|
|
|
check_show rfc2822 "$TIME" 'Wed, 15 Jun 2016 16:13:20 +0200'
|
|
|
|
check_show short "$TIME" '2016-06-15'
|
|
|
|
check_show default "$TIME" 'Wed Jun 15 16:13:20 2016 +0200'
|
|
|
|
check_show raw "$TIME" '1466000000 +0200'
|
2016-07-22 22:51:49 +03:00
|
|
|
check_show unix "$TIME" '1466000000'
|
2016-06-21 00:11:59 +03:00
|
|
|
check_show iso-local "$TIME" '2016-06-15 14:13:20 +0000'
|
date: document and test "raw-local" mode
The "raw" format shows a Unix epoch timestamp, but with a
timezone tacked on. The timestamp is not _in_ that zone, but
it is extra information about the time (by default, the zone
the author was in).
The documentation claims that "raw-local" does not work. It
does, but the end result is rather subtle. Let's describe it
in better detail, and test to make sure it works (namely,
the epoch time doesn't change, but the zone does).
While we are rewording the documentation in this area, let's
not use the phrase "does not work" for the remaining option,
"--date=relative". It's vague; do we accept it or not? We do
accept it, but it has no effect (which is a reasonable
outcome). We should also refer to the option not as
"--relative" (which is the historical synonym, and does not
take "-local" at all), but as "--date=relative".
Helped-by: Jakub Narębski <jnareb@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-07-27 16:44:41 +03:00
|
|
|
check_show raw-local "$TIME" '1466000000 +0000'
|
2016-07-22 22:51:49 +03:00
|
|
|
check_show unix-local "$TIME" '1466000000'
|
2016-06-21 00:11:59 +03:00
|
|
|
|
2017-06-15 15:29:53 +03:00
|
|
|
check_show 'format:%z' "$TIME" '+0200'
|
|
|
|
check_show 'format-local:%z' "$TIME" '+0000'
|
|
|
|
check_show 'format:%Z' "$TIME" ''
|
date: use localtime() for "-local" time formats
When we convert seconds-since-epochs timestamps into a
broken-down "struct tm", we do so by adjusting the timestamp
according to the known offset and then using gmtime() to
break down the result. This means that the resulting struct
"knows" that it's in GMT, even though the time it represents
is adjusted for a different zone. The fields where it stores
this data are not portably accessible, so we have no way to
override them to tell them the real zone info.
For the most part, this works. Our date-formatting routines
don't pay attention to these inaccessible fields, and use
the same tz info we provided for adjustment. The one
exception is when we call strftime(), whose %Z format
reveals this hidden timezone data.
We solved that by always showing the empty string for %Z.
This is allowed by POSIX, but not very helpful to the user.
We can't make this work in the general case, as there's no
portable function for setting an arbitrary timezone (and
anyway, we don't have the zone name for the author zones,
only their offsets).
But for the special case of the "-local" formats, we can
just skip the adjustment and use localtime() instead of
gmtime(). This makes --date=format-local:%Z work correctly,
showing the local timezone instead of an empty string.
The new test checks the result for "UTC", our default
test-lib value for $TZ. Using something like EST5 might be
more interesting, but the actual zone string is
system-dependent (for instance, on my system it expands to
just EST). Hopefully "UTC" is vanilla enough that every
system treats it the same.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-06-15 16:52:17 +03:00
|
|
|
check_show 'format-local:%Z' "$TIME" 'UTC'
|
2017-06-15 15:29:53 +03:00
|
|
|
check_show 'format:%%z' "$TIME" '%z'
|
|
|
|
check_show 'format-local:%%z' "$TIME" '%z'
|
|
|
|
|
2017-06-15 16:51:22 +03:00
|
|
|
check_show 'format:%Y-%m-%d %H:%M:%S' "$TIME" '2016-06-15 16:13:20'
|
|
|
|
check_show 'format-local:%Y-%m-%d %H:%M:%S' "$TIME" '2016-06-15 09:13:20' '' EST5
|
|
|
|
|
strbuf_addftime(): handle "%s" manually
The strftime() function has a non-standard "%s" extension, which prints
the number of seconds since the epoch. But the "struct tm" we get has
already been adjusted for a particular time zone; going back to an epoch
time requires knowing that zone offset. Since strftime() doesn't take
such an argument, round-tripping to a "struct tm" and back to the "%s"
format may produce the wrong value (off by tz_offset seconds).
Since we're already passing in the zone offset courtesy of c3fbf81a85
(strbuf: let strbuf_addftime handle %z and %Z itself, 2017-06-15), we
can use that same value to adjust our epoch seconds accordingly.
Note that the description above makes it sound like strftime()'s "%s" is
useless (and really, the issue is shared by mktime(), which is what
strftime() would use under the hood). But it gets the two cases for
which it's designed correct:
- the result of gmtime() will have a zero offset, so no adjustment is
necessary
- the result of localtime() will be offset by the local zone offset,
and mktime() and strftime() are defined to assume this offset when
converting back (there's actually some magic here; some
implementations record this in the "struct tm", but we can't
portably access or manipulate it. But they somehow "know" whether a
"struct tm" is from gmtime() or localtime()).
This latter point means that "format-local:%s" actually works correctly
already, because in that case we rely on the system routines due to
6eced3ec5e (date: use localtime() for "-local" time formats,
2017-06-15). Our problem comes when trying to show times in the author's
zone, as the system routines provide no mechanism for converting in
non-local zones. So in those cases we have a "struct tm" that came from
gmtime(), but has been manipulated according to our offset.
The tests cover the broken round-trip by formatting "%s" for a time in a
non-system timezone. We use the made-up "+1234" here, which has two
advantages. One, we know it won't ever be the real system zone (and so
we're actually testing a case that would break). And two, since it has a
minute component, we're testing the full decoding of the +HHMM zone into
a number of seconds. Likewise, we test the "-1234" variant to make sure
there aren't any sign mistakes.
There's one final test, which covers "format-local:%s". As noted, this
already passes, but it's important to check that we didn't regress this
case. In particular, the caller in show_date() is relying on localtime()
to have done the zone adjustment, independent of any tz_offset we
compute ourselves. These should match up, since our local_tzoffset() is
likewise built around localtime(). But it would be easy for a caller to
forget to pass in a correct tz_offset to strbuf_addftime(). Fortunately
show_date() does this correctly (it has to because of the existing
handling of %z), and the test continues to pass. So this one is just
future-proofing against a change in our assumptions.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-11-02 14:35:34 +03:00
|
|
|
check_show 'format:%s' '123456789 +1234' 123456789
|
|
|
|
check_show 'format:%s' '123456789 -1234' 123456789
|
|
|
|
check_show 'format-local:%s' '123456789 -1234' 123456789
|
|
|
|
|
local_tzoffset: detect errors from tm_to_time_t
When we want to know the local timezone offset at a given
timestamp, we compute it by asking for localtime() at the
given time, and comparing the offset to GMT at that time.
However, there's some juggling between time_t and "struct
tm" which happens, which involves calling our own
tm_to_time_t().
If that function returns an error (e.g., because it only
handles dates up to the year 2099), it returns "-1", which
we treat as a time_t, and is clearly bogus, leading to
bizarre timestamps (that seem to always adjust the time back
to (time_t)(uint32_t)-1, in the year 2106).
It's not a good idea for local_tzoffset() to simply die
here; it would make it hard to run "git log" on a repository
with funny timestamps. Instead, let's just treat such cases
as "zero offset".
Reported-by: Norbert Kiesel <nkiesel@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-21 00:14:14 +03:00
|
|
|
# arbitrary time absurdly far in the future
|
|
|
|
FUTURE="5758122296 -0400"
|
2017-04-20 23:58:21 +03:00
|
|
|
check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT
|
|
|
|
check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT,TIME_T_IS_64BIT
|
local_tzoffset: detect errors from tm_to_time_t
When we want to know the local timezone offset at a given
timestamp, we compute it by asking for localtime() at the
given time, and comparing the offset to GMT at that time.
However, there's some juggling between time_t and "struct
tm" which happens, which involves calling our own
tm_to_time_t().
If that function returns an error (e.g., because it only
handles dates up to the year 2099), it returns "-1", which
we treat as a time_t, and is clearly bogus, leading to
bizarre timestamps (that seem to always adjust the time back
to (time_t)(uint32_t)-1, in the year 2106).
It's not a good idea for local_tzoffset() to simply die
here; it would make it hard to run "git log" on a repository
with funny timestamps. Instead, let's just treat such cases
as "zero offset".
Reported-by: Norbert Kiesel <nkiesel@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-21 00:14:14 +03:00
|
|
|
|
2009-08-31 06:30:16 +04:00
|
|
|
check_parse() {
|
|
|
|
echo "$1 -> $2" >expect
|
2010-07-04 14:48:35 +04:00
|
|
|
test_expect_${4:-success} "parse date ($1${3:+ TZ=$3})" "
|
2018-03-24 10:44:36 +03:00
|
|
|
TZ=${3:-$TZ} test-tool date parse '$1' >actual &&
|
2009-08-31 06:30:16 +04:00
|
|
|
test_cmp expect actual
|
|
|
|
"
|
|
|
|
}
|
|
|
|
|
|
|
|
check_parse 2008 bad
|
|
|
|
check_parse 2008-02 bad
|
|
|
|
check_parse 2008-02-14 bad
|
|
|
|
check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
|
2010-07-04 14:48:35 +04:00
|
|
|
check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
|
2020-04-24 18:07:31 +03:00
|
|
|
check_parse '2008.02.14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
|
2020-04-24 18:07:32 +03:00
|
|
|
check_parse '20080214T203045-04:00' '2008-02-14 20:30:45 -0400'
|
|
|
|
check_parse '20080214T203045 -04:00' '2008-02-14 20:30:45 -0400'
|
|
|
|
check_parse '20080214T203045.019-04:00' '2008-02-14 20:30:45 -0400'
|
2020-04-24 18:07:31 +03:00
|
|
|
check_parse '2008-02-14 20:30:45.019-04:00' '2008-02-14 20:30:45 -0400'
|
2011-09-09 14:10:33 +04:00
|
|
|
check_parse '2008-02-14 20:30:45 -0015' '2008-02-14 20:30:45 -0015'
|
|
|
|
check_parse '2008-02-14 20:30:45 -5' '2008-02-14 20:30:45 +0000'
|
|
|
|
check_parse '2008-02-14 20:30:45 -5:' '2008-02-14 20:30:45 +0000'
|
|
|
|
check_parse '2008-02-14 20:30:45 -05' '2008-02-14 20:30:45 -0500'
|
|
|
|
check_parse '2008-02-14 20:30:45 -:30' '2008-02-14 20:30:45 +0000'
|
|
|
|
check_parse '2008-02-14 20:30:45 -05:00' '2008-02-14 20:30:45 -0500'
|
2010-07-07 03:34:20 +04:00
|
|
|
check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 -0500' EST5
|
2009-08-31 06:30:16 +04:00
|
|
|
|
|
|
|
check_approxidate() {
|
|
|
|
echo "$1 -> $2 +0000" >expect
|
|
|
|
test_expect_${3:-success} "parse approxidate ($1)" "
|
2018-03-24 10:44:36 +03:00
|
|
|
test-tool date approxidate '$1' >actual &&
|
2009-08-31 06:30:16 +04:00
|
|
|
test_cmp expect actual
|
|
|
|
"
|
|
|
|
}
|
|
|
|
|
|
|
|
check_approxidate now '2009-08-30 19:20:00'
|
|
|
|
check_approxidate '5 seconds ago' '2009-08-30 19:19:55'
|
|
|
|
check_approxidate 5.seconds.ago '2009-08-30 19:19:55'
|
|
|
|
check_approxidate 10.minutes.ago '2009-08-30 19:10:00'
|
|
|
|
check_approxidate yesterday '2009-08-29 19:20:00'
|
|
|
|
check_approxidate 3.days.ago '2009-08-27 19:20:00'
|
2020-04-24 18:07:31 +03:00
|
|
|
check_approxidate '12:34:56.3.days.ago' '2009-08-27 12:34:56'
|
2009-08-31 06:30:16 +04:00
|
|
|
check_approxidate 3.weeks.ago '2009-08-09 19:20:00'
|
2009-08-31 06:31:42 +04:00
|
|
|
check_approxidate 3.months.ago '2009-05-30 19:20:00'
|
|
|
|
check_approxidate 2.years.3.months.ago '2007-05-30 19:20:00'
|
2009-08-31 06:30:16 +04:00
|
|
|
|
|
|
|
check_approxidate '6am yesterday' '2009-08-29 06:00:00'
|
|
|
|
check_approxidate '6pm yesterday' '2009-08-29 18:00:00'
|
|
|
|
check_approxidate '3:00' '2009-08-30 03:00:00'
|
|
|
|
check_approxidate '15:00' '2009-08-30 15:00:00'
|
|
|
|
check_approxidate 'noon today' '2009-08-30 12:00:00'
|
|
|
|
check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
|
approxidate: handle pending number for "specials"
The approxidate parser has a table of special keywords like
"yesterday", "noon", "pm", etc. Some of these, like "pm", do
the right thing if we've recently seen a number: "3pm" is
what you'd think.
However, most of them do not look at or modify the
pending-number flag at all, which means a number may "jump"
across a significant keyword and be used unexpectedly. For
example, when parsing:
January 5th noon pm
we'd connect the "5" to "pm", and ignore it as a
day-of-month. This is obviously a bit silly, as "noon"
already implies "pm". And other mis-parsed things are
generally as silly ("January 5th noon, years ago" would
connect the 5 to "years", but probably nobody would type
that).
However, the fix is simple: when we see a keyword like
"noon", we should flush the pending number (as we would if
we hit another number, or the end of the string). In a few
of the specials that actually modify the day, we can simply
throw away the number (saying "Jan 5 yesterday" should not
respect the number at all).
Note that we have to either move or forward-declare the
static pending_number() to make it accessible to these
functions; this patch moves it.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-02 08:23:09 +03:00
|
|
|
check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'
|
approxidate: fix NULL dereference in date_time()
When we see a time like "noon", we pass "12" to our date_time() helper,
which sets the hour to 12pm. If the current time is before noon, then we
wrap around to yesterday using date_yesterday(). But unlike the normal
calls to date_yesterday() from approxidate_alpha(), we pass a NULL "num"
parameter. Since c27cc94fad (approxidate: handle pending number for
"specials", 2018-11-02), that causes a segfault.
One way to fix this is by checking for NULL. But arguably date_time() is
abusing our helper by passing NULL in the first place (and this is the
only case where one of these "special" parsers is used this way). So
instead, let's have it just do the 1-day subtraction itself. It's still
just a one-liner due to our update_tm() helper.
Note that the test added here is a little funny, as we say "10am noon",
which makes the "10am" seem pointless. But this bug can only be
triggered when it the currently-parsed hour is before the special time.
The latest special time is "tea" at 1700, but t0006 uses a hard-coded
TEST_DATE_NOW of 1900. We could reset TEST_DATE_NOW, but that may lead
to confusion in other tests. Just saying "10am noon" makes this test
self-contained.
Reported-by: Carlo Arenas <carenas@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-07 04:12:53 +03:00
|
|
|
check_approxidate '10am noon' '2009-08-29 12:00:00'
|
2009-08-31 06:30:16 +04:00
|
|
|
|
|
|
|
check_approxidate 'last tuesday' '2009-08-25 19:20:00'
|
|
|
|
check_approxidate 'July 5th' '2009-07-05 19:20:00'
|
|
|
|
check_approxidate '06/05/2009' '2009-06-05 19:20:00'
|
|
|
|
check_approxidate '06.05.2009' '2009-05-06 19:20:00'
|
|
|
|
|
|
|
|
check_approxidate 'Jun 6, 5AM' '2009-06-06 05:00:00'
|
|
|
|
check_approxidate '5AM Jun 6' '2009-06-06 05:00:00'
|
|
|
|
check_approxidate '6AM, June 7, 2009' '2009-06-07 06:00:00'
|
|
|
|
|
approxidate: allow ISO-like dates far in the future
When we are parsing approxidate strings and we find three
numbers separate by one of ":/-.", we guess that it may be a
date. We feed the numbers to match_multi_number, which
checks whether it makes sense as a date in various orderings
(e.g., dd/mm/yy or mm/dd/yy, etc).
One of the checks we do is to see whether it is a date more
than 10 days in the future. This was added in 38035cf (date
parsing: be friendlier to our European friends.,
2006-04-05), and lets us guess that if it is currently April
2014, then "10/03/2014" is probably March 10th, not October
3rd.
This has a downside, though; if you want to be overly
generous with your "--until" date specification, we may
wrongly parse "2014-12-01" as "2014-01-12" (because the
latter is an in-the-past date). If the year is a future year
(i.e., both are future dates), it gets even weirder. Due to
the vagaries of approxidate, months _after_ the current date
(no matter the year) get flipped, but ones before do not.
This patch drops the "in the future" check for dates of this
form, letting us treat them always as yyyy-mm-dd, even if
they are in the future. This does not affect the normal
dd/mm/yyyy versus mm/dd/yyyy lookup, because this code path
only kicks in when the first number is greater than 70
(i.e., it must be a year, and cannot be either a date or a
month).
The one possible casualty is that "yyyy-dd-mm" is less
likely to be chosen over "yyyy-mm-dd". That's probably OK,
though because:
1. The difference happens only when the date is in the
future. Already we prefer yyyy-mm-dd for dates in the
past.
2. It's unclear whether anybody even uses yyyy-dd-mm
regularly. It does not appear in lists of common date
formats in Wikipedia[1,2].
3. Even if (2) is wrong, it is better to prefer ISO-like
dates, as that is consistent with what we use elsewhere
in git.
[1] http://en.wikipedia.org/wiki/Date_and_time_representation_by_country
[2] http://en.wikipedia.org/wiki/Calendar_date
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-11-14 00:43:31 +03:00
|
|
|
check_approxidate '2008-12-01' '2008-12-01 19:20:00'
|
|
|
|
check_approxidate '2009-12-01' '2009-12-01 19:20:00'
|
|
|
|
|
2019-01-29 06:50:16 +03:00
|
|
|
check_date_format_human() {
|
|
|
|
t=$(($GIT_TEST_DATE_NOW - $1))
|
|
|
|
echo "$t -> $2" >expect
|
|
|
|
test_expect_success "human date $t" '
|
|
|
|
test-tool date human $t >actual &&
|
2021-02-11 04:53:53 +03:00
|
|
|
test_cmp expect actual
|
2019-01-29 06:50:16 +03:00
|
|
|
'
|
|
|
|
}
|
|
|
|
|
|
|
|
check_date_format_human 18000 "5 hours ago" # 5 hours ago
|
|
|
|
check_date_format_human 432000 "Tue Aug 25 19:20" # 5 days ago
|
|
|
|
check_date_format_human 1728000 "Mon Aug 10 19:20" # 3 weeks ago
|
|
|
|
check_date_format_human 13000000 "Thu Apr 2 08:13" # 5 months ago
|
|
|
|
check_date_format_human 31449600 "Aug 31 2008" # 12 months ago
|
|
|
|
check_date_format_human 37500000 "Jun 22 2008" # 1 year, 2 months ago
|
|
|
|
check_date_format_human 55188000 "Dec 1 2007" # 1 year, 9 months ago
|
|
|
|
check_date_format_human 630000000 "Sep 13 1989" # 20 years ago
|
|
|
|
|
2009-08-31 06:30:16 +04:00
|
|
|
test_done
|