* [PATCH v3 1/4] t0006: add support for approxidate test date adjustment
2026-05-14 11:55 ` [PATCH v3 0/4] approxidate: tweak special date formats Tuomas Ahola
@ 2026-05-14 11:55 ` Tuomas Ahola
2026-05-14 11:55 ` [PATCH v3 2/4] approxidate: alias "today" to "now" Tuomas Ahola
` (3 subsequent siblings)
4 siblings, 0 replies; 28+ messages in thread
From: Tuomas Ahola @ 2026-05-14 11:55 UTC (permalink / raw)
To: git; +Cc: Jeff King, Junio C Hamano, Tuomas Ahola
t0006 uses a hard-coded test date and provides no convenient
way to override it temporarily. Add an optional parameter to
check_approxidate to adjust the time as needed, and demonstrate
the feature with a new test.
Signed-off-by: Tuomas Ahola <taahol@utu.fi>
---
Notes:
> As you are not doing the test-date-now adjustment when $4 is not
> given, wouldn't it be a lot easier to read if you did something like
>
> old_date=$GIT_TEST_DATE_NOW
> if test -n "$4"
> then
> # the convention for $4 is a bit weird in that it
> # comes with its own +/- operator in front.
> GIT_TEST_DATE_NOW=$(( $old_date $4 * 60 * 60 ))
> caption="$1; offset $4h"
> else
> caption=$1
> fi
>
> instead? Other two minor points are
>
> - Documentation/SubmittingPatches prefers an explicit "test" over
> "[ ... ]", and have "then", "else", etc. on their own lines.
>
> - As you never "unset" GIT_TEST_DATE_NOW, you do not have to keep
> exporting it. It is not like there are two variables (one for
> shell, the other for environment) and every time you set the
> shell one you need to export to reflect the value to the
> environment one. Rather, a single "export" marks a shell
> variable and every time it changes value, it is updated in the
> environment as well.
>
Thanks, applied.
> One, it sucks to have to say "success" here, but is awkward because now
> we have two optional arguments. There's nobody passing "failure" right
> now, so we could just drop support, though that might be annoying later
> when somebody wants to add a failing test. But we could perhaps switch
> to allowing:
>
Ok, now it works without "success" in between, and "failure" works without
an offset, too.
check_approxidate <test-string> <expected-result> [<test-time-offset>] [failure]
> The second thing is that "+48" is pretty opaque. It's a relative offset
> to some arbitrary point. To some degree the script already suffers from
> that (all of the tests are using some arbitrary point), but I think the
> offset (without units!) adds a layer of indirection that makes it even
> more confusing.
>
Good catch. Now it is at least marginally better with the added units.
t/t0006-date.sh | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 53ced36df4..c7667bade2 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -155,12 +155,41 @@ check_parse '2100-00-00 00:00:00 -11' bad
check_parse '2100-00-00 00:00:00 +11' bad
REQUIRE_64BIT_TIME=
+add_time_offset() {
+ case "$3" in
+ hours)
+ unit=$(( 60*60 ))
+ ;;
+ days)
+ unit=$(( 24*60*60 ))
+ ;;
+ esac
+ offset=$(( $2 * unit ))
+ echo $(( $1 + offset ))
+}
+
check_approxidate() {
+ old_date=$GIT_TEST_DATE_NOW
+ if test "$3" = "failure"
+ then
+ expection="$3"
+ else
+ expection=${4:-success}
+ offset="$3"
+ fi
+ if test -n "$offset"
+ then
+ GIT_TEST_DATE_NOW=$(add_time_offset $old_date $offset)
+ caption="$1; offset $offset"
+ else
+ caption=$1
+ fi
echo "$1 -> $2 +0000" >expect
- test_expect_${3:-success} "parse approxidate ($1)" "
+ test_expect_$expection "parse approxidate ($caption)" "
test-tool date approxidate '$1' >actual &&
test_cmp expect actual
"
+ GIT_TEST_DATE_NOW=$old_date
}
check_approxidate now '2009-08-30 19:20:00'
@@ -182,6 +211,8 @@ check_approxidate 'noon today' '2009-08-30 12:00:00'
check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'
check_approxidate '10am noon' '2009-08-29 12:00:00'
+check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'
+check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'
check_approxidate 'last tuesday' '2009-08-25 19:20:00'
check_approxidate 'July 5th' '2009-07-05 19:20:00'
--
2.30.2
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v3 2/4] approxidate: alias "today" to "now"
2026-05-14 11:55 ` [PATCH v3 0/4] approxidate: tweak special date formats Tuomas Ahola
2026-05-14 11:55 ` [PATCH v3 1/4] t0006: add support for approxidate test date adjustment Tuomas Ahola
@ 2026-05-14 11:55 ` Tuomas Ahola
2026-05-14 15:36 ` Junio C Hamano
2026-05-14 11:55 ` [PATCH v3 3/4] approxidate: make "specials" respect fixed day-of-month Tuomas Ahola
` (2 subsequent siblings)
4 siblings, 1 reply; 28+ messages in thread
From: Tuomas Ahola @ 2026-05-14 11:55 UTC (permalink / raw)
To: git; +Cc: Jeff King, Junio C Hamano, Tuomas Ahola
As far as approxidate in concerned, "today" is a no-op. That makes
it functionally equivalent to "now" in commands like
$ git log --since=today
Make that behavior explicit by binding "today" to `date_now()`.
That way later patches can give "today" some functionality in
cases like "today at noon".
Signed-off-by: Tuomas Ahola <taahol@utu.fi>
---
Notes:
> Hmph, this may not work very well for "git log --since=today", which
> you may want to stop immediately when the traversal reaches a patch
> written before the most recent midnight, instead of stopping without
> giving anything back.
>
Sorry, I don't know if I understood. Does the patch change the behavior of
that command somehow? Is there some kind of edge case I missed?
That said, if we do want to change it so that "git log --since=today"
worked like "--since=midnight", this seems to do the trick:
```
static void date_today(struct tm *tm, struct tm *now, int *num)
{
if (tm->tm_hour == now->tm_hour &&
tm->tm_min == now->tm_min &&
tm->tm_sec == now->tm_sec)
date_midnight(tm, now, num);
date_now(tm, now, num);
}
```
date.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/date.c b/date.c
index 17a95077cf..412aca6dc4 100644
--- a/date.c
+++ b/date.c
@@ -1204,6 +1204,7 @@ static const struct special {
{ "AM", date_am },
{ "never", date_never },
{ "now", date_now },
+ { "today", date_now },
{ NULL }
};
--
2.30.2
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH v3 2/4] approxidate: alias "today" to "now"
2026-05-14 11:55 ` [PATCH v3 2/4] approxidate: alias "today" to "now" Tuomas Ahola
@ 2026-05-14 15:36 ` Junio C Hamano
2026-05-14 21:07 ` Tuomas Ahola
0 siblings, 1 reply; 28+ messages in thread
From: Junio C Hamano @ 2026-05-14 15:36 UTC (permalink / raw)
To: Tuomas Ahola; +Cc: git, Jeff King
Tuomas Ahola <taahol@utu.fi> writes:
> Sorry, I don't know if I understood. Does the patch change the behavior of
> that command somehow? Is there some kind of edge case I missed?
No, I did not think it was a good idea to carve the behaviour in
stone that "git log --since=today" behaves as if it were given "git
log --since=now". My reaction would have been very different if we
were deliberatly and explicitly saying "today is synonym for now",
but the thing is, it is not a designed behaviour but what
approxidate does for anything it does not understand, e.g.
git log --since=decay
git log --since=bogus
all behave as if it were given --since=now.
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH v3 2/4] approxidate: alias "today" to "now"
2026-05-14 15:36 ` Junio C Hamano
@ 2026-05-14 21:07 ` Tuomas Ahola
2026-05-15 1:27 ` Junio C Hamano
0 siblings, 1 reply; 28+ messages in thread
From: Tuomas Ahola @ 2026-05-14 21:07 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Jeff King
Junio C Hamano <gitster@pobox.com> wrote:
> Tuomas Ahola <taahol@utu.fi> writes:
>
> > Sorry, I don't know if I understood. Does the patch change the behavior of
> > that command somehow? Is there some kind of edge case I missed?
>
> No, I did not think it was a good idea to carve the behaviour in
> stone that "git log --since=today" behaves as if it were given "git
> log --since=now". My reaction would have been very different if we
> were deliberatly and explicitly saying "today is synonym for now",
> but the thing is, it is not a designed behaviour but what
> approxidate does for anything it does not understand, e.g.
>
> git log --since=decay
> git log --since=bogus
>
> all behave as if it were given --since=now.
Thanks for spelling that out. So, as there is no deliberative
decision behind the current behaviour of "today", the code has
to remain non-committed on that; we are not at liberty to codify
the status quo. Right? But perhaps we can find a least common
denominator. "Today" means the current day (well, obviously),
and this seems to be enough to get "today at noon" and such to work:
```
static void date_today(struct tm *tm, struct tm *now, int *num)
{
*num = 0;
tm->tm_mday = now->tm_mday;
}
```
It gets the job done whitout setting in stone too much anything
that is up for debate. Do you see that could work?
--Tuomas
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH v3 2/4] approxidate: alias "today" to "now"
2026-05-14 21:07 ` Tuomas Ahola
@ 2026-05-15 1:27 ` Junio C Hamano
2026-05-15 1:38 ` Junio C Hamano
0 siblings, 1 reply; 28+ messages in thread
From: Junio C Hamano @ 2026-05-15 1:27 UTC (permalink / raw)
To: Tuomas Ahola; +Cc: git, Jeff King
Tuomas Ahola <taahol@utu.fi> writes:
> Junio C Hamano <gitster@pobox.com> wrote:
>
>> Tuomas Ahola <taahol@utu.fi> writes:
>>
>> > Sorry, I don't know if I understood. Does the patch change the behavior of
>> > that command somehow? Is there some kind of edge case I missed?
>>
>> No, I did not think it was a good idea to carve the behaviour in
>> stone that "git log --since=today" behaves as if it were given "git
>> log --since=now". My reaction would have been very different if we
>> were deliberatly and explicitly saying "today is synonym for now",
>> but the thing is, it is not a designed behaviour but what
>> approxidate does for anything it does not understand, e.g.
>>
>> git log --since=decay
>> git log --since=bogus
>>
>> all behave as if it were given --since=now.
>
> Thanks for spelling that out. So, as there is no deliberative
> decision behind the current behaviour of "today", the code has
> to remain non-committed on that; we are not at liberty to codify
> the status quo. Right?
Not right. It is more like "Even though we try not to change
existing behavoiur left and right without a good reason to avoid
breaking existing users' expectations, we should be able to "fix"
what is not intended behaviour but is something the code happened to
be doing, especially if the current behaviour does not make sense.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v3 2/4] approxidate: alias "today" to "now"
2026-05-15 1:27 ` Junio C Hamano
@ 2026-05-15 1:38 ` Junio C Hamano
2026-05-15 5:02 ` Tuomas Ahola
0 siblings, 1 reply; 28+ messages in thread
From: Junio C Hamano @ 2026-05-15 1:38 UTC (permalink / raw)
To: Tuomas Ahola; +Cc: git, Jeff King
Junio C Hamano <gitster@pobox.com> writes:
> Tuomas Ahola <taahol@utu.fi> writes:
>
>> Junio C Hamano <gitster@pobox.com> wrote:
>>
>>> Tuomas Ahola <taahol@utu.fi> writes:
>>>
>>> > Sorry, I don't know if I understood. Does the patch change the behavior of
>>> > that command somehow? Is there some kind of edge case I missed?
>>>
>>> No, I did not think it was a good idea to carve the behaviour in
>>> stone that "git log --since=today" behaves as if it were given "git
>>> log --since=now". My reaction would have been very different if we
>>> were deliberatly and explicitly saying "today is synonym for now",
>>> but the thing is, it is not a designed behaviour but what
>>> approxidate does for anything it does not understand, e.g.
>>>
>>> git log --since=decay
>>> git log --since=bogus
>>>
>>> all behave as if it were given --since=now.
>>
>> Thanks for spelling that out. So, as there is no deliberative
>> decision behind the current behaviour of "today", the code has
>> to remain non-committed on that; we are not at liberty to codify
>> the status quo. Right?
>
> Not right. It is more like "Even though we try not to change
> existing behavoiur left and right without a good reason to avoid
> breaking existing users' expectations, we should be able to "fix"
> what is not intended behaviour but is something the code happened to
> be doing, especially if the current behaviour does not make sense.
And the other half of the discussion is that once we explicitly say
"today means right now" and make it official, it makes it much harder
to fix it later. So we need to be very careful in our first attempt.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v3 2/4] approxidate: alias "today" to "now"
2026-05-15 1:38 ` Junio C Hamano
@ 2026-05-15 5:02 ` Tuomas Ahola
0 siblings, 0 replies; 28+ messages in thread
From: Tuomas Ahola @ 2026-05-15 5:02 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Jeff King
Junio C Hamano <gitster@pobox.com> wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>
> > Tuomas Ahola <taahol@utu.fi> writes:
> >
> >> Junio C Hamano <gitster@pobox.com> wrote:
> >>
> >>> Tuomas Ahola <taahol@utu.fi> writes:
> >>>
> >>> > Sorry, I don't know if I understood. Does the patch change the behavior of
> >>> > that command somehow? Is there some kind of edge case I missed?
> >>>
> >>> No, I did not think it was a good idea to carve the behaviour in
> >>> stone that "git log --since=today" behaves as if it were given "git
> >>> log --since=now". My reaction would have been very different if we
> >>> were deliberatly and explicitly saying "today is synonym for now",
> >>> but the thing is, it is not a designed behaviour but what
> >>> approxidate does for anything it does not understand, e.g.
> >>>
> >>> git log --since=decay
> >>> git log --since=bogus
> >>>
> >>> all behave as if it were given --since=now.
> >>
> >> Thanks for spelling that out. So, as there is no deliberative
> >> decision behind the current behaviour of "today", the code has
> >> to remain non-committed on that; we are not at liberty to codify
> >> the status quo. Right?
> >
> > Not right. It is more like "Even though we try not to change
> > existing behavoiur left and right without a good reason to avoid
> > breaking existing users' expectations, we should be able to "fix"
> > what is not intended behaviour but is something the code happened to
> > be doing, especially if the current behaviour does not make sense.
Well, that's not far off from what I wrote. I just meant we cannot
make the current (somewhat accidental) behaviour official *just because*
it happens to be the status quo.
>
> And the other half of the discussion is that once we explicitly say "today
> means right now" and make it official, it makes it much harder to fix it
> later. So we need to be very careful in our first attempt.
Roger that.
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v3 3/4] approxidate: make "specials" respect fixed day-of-month
2026-05-14 11:55 ` [PATCH v3 0/4] approxidate: tweak special date formats Tuomas Ahola
2026-05-14 11:55 ` [PATCH v3 1/4] t0006: add support for approxidate test date adjustment Tuomas Ahola
2026-05-14 11:55 ` [PATCH v3 2/4] approxidate: alias "today" to "now" Tuomas Ahola
@ 2026-05-14 11:55 ` Tuomas Ahola
2026-05-14 16:06 ` Junio C Hamano
2026-05-14 11:55 ` [PATCH v3 4/4] approxidate: use deferred mday adjustments for "specials" Tuomas Ahola
2026-05-16 15:15 ` [PATCH v4 0/4] approxidate: tweak special date formats Tuomas Ahola
4 siblings, 1 reply; 28+ messages in thread
From: Tuomas Ahola @ 2026-05-14 11:55 UTC (permalink / raw)
To: git; +Cc: Jeff King, Junio C Hamano, Tuomas Ahola
The special approxidate time formats, "noon" and "tea" differ from
"12pm" and "5pm" by having the feature of wrapping to the previous day
if the current time is before those hours:
now -> 2026-05-13 11:00:00 +0000
12pm -> 2026-05-13 12:00:00 +0000
5pm -> 2026-05-13 17:00:00 +0000
noon -> 2026-05-12 12:00:00 +0000
tea -> 2026-05-12 17:00:00 +0000
However, that logic carries too far. Even when the date is specified,
the behavior of the "specials" depends on the current time. Assuming
the same time as above, we get:
today at noon -> 2026-05-12 12:00:00 +0000 (should be 13 May)
13 May at tea -> 2026-05-12 17:00:00 +0000
or, using an example mentioned in date-formats.adoc:
last Friday at noon -> 2026-05-07 12:00:00 +0000 (should be 8 May)
The quirk seems to be rather old. Already in 2006, Linus Torvalds
remarked that the date yielded by "one year ago yesterday at tea-time"
was "just silly and not even correct". Indeed, even today it gives:
One year ago yesterday at tea-time -> 2025-05-11 17:00:00 +0000
(should be 12 May)
Let's fix all of those with a simple patch. Check whether we already
have a specified day-of-month in `tm->tm_mday` and make `date_time()`
stick to it. Ensure the correct behavior with relevant tests.
Links:
1. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/
Signed-off-by: Tuomas Ahola <taahol@utu.fi>
---
Notes:
> Again, this may be just me, but I happen to find the version of
> comment in Peff's review on the earlier iteration of this series
> much easier to understand.
>
Thanks, applied.
date.c | 6 +++++-
t/t0006-date.sh | 4 ++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/date.c b/date.c
index 412aca6dc4..73879d202c 100644
--- a/date.c
+++ b/date.c
@@ -1132,7 +1132,11 @@ static void date_yesterday(struct tm *tm, struct tm *now, int *num)
static void date_time(struct tm *tm, struct tm *now, int hour)
{
- if (tm->tm_hour < hour)
+ /*
+ * If we do not yet have a specified day, we'll use the most recent
+ * version of "hour" relative to now. But that may be yesterday.
+ */
+ if (tm->tm_mday < 0 && tm->tm_hour < hour)
update_tm(tm, now, 24*60*60);
tm->tm_hour = hour;
tm->tm_min = 0;
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index c7667bade2..d800cb30fe 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -208,8 +208,12 @@ 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 'today at noon' '2009-08-30 12:00:00' '-12 hours'
check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'
+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'
check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'
+check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'
check_approxidate '10am noon' '2009-08-29 12:00:00'
check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'
check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'
--
2.30.2
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH v3 3/4] approxidate: make "specials" respect fixed day-of-month
2026-05-14 11:55 ` [PATCH v3 3/4] approxidate: make "specials" respect fixed day-of-month Tuomas Ahola
@ 2026-05-14 16:06 ` Junio C Hamano
0 siblings, 0 replies; 28+ messages in thread
From: Junio C Hamano @ 2026-05-14 16:06 UTC (permalink / raw)
To: Tuomas Ahola; +Cc: git, Jeff King
Tuomas Ahola <taahol@utu.fi> writes:
> The special approxidate time formats, "noon" and "tea" differ from
> "12pm" and "5pm" by having the feature of wrapping to the previous day
> if the current time is before those hours:
>
> now -> 2026-05-13 11:00:00 +0000
>
> 12pm -> 2026-05-13 12:00:00 +0000
> 5pm -> 2026-05-13 17:00:00 +0000
>
> noon -> 2026-05-12 12:00:00 +0000
> tea -> 2026-05-12 17:00:00 +0000
>
> However, that logic carries too far. Even when the date is specified,
> the behavior of the "specials" depends on the current time. Assuming
> the same time as above, we get:
>
> today at noon -> 2026-05-12 12:00:00 +0000 (should be 13 May)
> 13 May at tea -> 2026-05-12 17:00:00 +0000
>
> or, using an example mentioned in date-formats.adoc:
>
> last Friday at noon -> 2026-05-07 12:00:00 +0000 (should be 8 May)
>
> The quirk seems to be rather old. Already in 2006, Linus Torvalds
> remarked that the date yielded by "one year ago yesterday at tea-time"
> was "just silly and not even correct". Indeed, even today it gives:
>
> One year ago yesterday at tea-time -> 2025-05-11 17:00:00 +0000
> (should be 12 May)
>
> Let's fix all of those with a simple patch. Check whether we already
> have a specified day-of-month in `tm->tm_mday` and make `date_time()`
> stick to it. Ensure the correct behavior with relevant tests.
I find this vastly easier to follow the reasoning, compared to the
previous iteration. Very nicely done.
>
> Links:
> 1. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/
>
> Signed-off-by: Tuomas Ahola <taahol@utu.fi>
> ---
>
> Notes:
> > Again, this may be just me, but I happen to find the version of
> > comment in Peff's review on the earlier iteration of this series
> > much easier to understand.
> >
>
> Thanks, applied.
>
> date.c | 6 +++++-
> t/t0006-date.sh | 4 ++++
> 2 files changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/date.c b/date.c
> index 412aca6dc4..73879d202c 100644
> --- a/date.c
> +++ b/date.c
> @@ -1132,7 +1132,11 @@ static void date_yesterday(struct tm *tm, struct tm *now, int *num)
>
> static void date_time(struct tm *tm, struct tm *now, int hour)
> {
> - if (tm->tm_hour < hour)
> + /*
> + * If we do not yet have a specified day, we'll use the most recent
> + * version of "hour" relative to now. But that may be yesterday.
> + */
> + if (tm->tm_mday < 0 && tm->tm_hour < hour)
> update_tm(tm, now, 24*60*60);
> tm->tm_hour = hour;
> tm->tm_min = 0;
> diff --git a/t/t0006-date.sh b/t/t0006-date.sh
> index c7667bade2..d800cb30fe 100755
> --- a/t/t0006-date.sh
> +++ b/t/t0006-date.sh
> @@ -208,8 +208,12 @@ 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 'today at noon' '2009-08-30 12:00:00' '-12 hours'
> check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
> +check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'
> +check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'
> check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'
> +check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'
> check_approxidate '10am noon' '2009-08-29 12:00:00'
> check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'
> check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v3 4/4] approxidate: use deferred mday adjustments for "specials"
2026-05-14 11:55 ` [PATCH v3 0/4] approxidate: tweak special date formats Tuomas Ahola
` (2 preceding siblings ...)
2026-05-14 11:55 ` [PATCH v3 3/4] approxidate: make "specials" respect fixed day-of-month Tuomas Ahola
@ 2026-05-14 11:55 ` Tuomas Ahola
2026-05-16 15:15 ` [PATCH v4 0/4] approxidate: tweak special date formats Tuomas Ahola
4 siblings, 0 replies; 28+ messages in thread
From: Tuomas Ahola @ 2026-05-14 11:55 UTC (permalink / raw)
To: git; +Cc: Jeff King, Junio C Hamano, Tuomas Ahola
There are cases where the "wrap-to-yesterday" behavior of "tea" and
"noon" should be reverted later on down the line, so that "today tea"
and "tea today" won't yield different results. However, the logic of
approxidate doesn't seem to lend itself particularly well to
such cases.
Start tackling the issue by reusing negative values of `tm->tm_mday`
field for deferred date adjustments which can be easily reverted, so
that the default logic of the special formats only applies if we don't
get any explicit date (mday) specification. In particular, overwrite
the field with -1 in "now" and "yesterday", so that those formats will
be relative to the current date. That makes specifications like "tea
yesterday" behave more sensibly: instead of going backwards to the
last tea-time and then a day back, Git will now understand that as the
tea-time of yesterday.
Replace the call of `update_tm()` in `date_time()` with the assignment
`tm->tm_mday = -2`. Add the corresponding code to handle that in
`update_tm()`, wrapping to the previous day if the field still holds
such assignment, meaning that we haven't seen any better specification
for the day-of-month. On the other hand, `mday=-3` would mean going
two days back and so on. Even though such functionality isn't
actually needed by this patch, it won't add much complexity in the
code and is rather natural way to handle such values.
As `date_time()` won't no longer need the `now` struct, mark the
associated function parameters as unused. The parameters themselves
have to stay, however, as those functions are called through pointers
in `approxidate_alpha`. Add relevant tests to cover the changes.
Signed-off-by: Tuomas Ahola <taahol@utu.fi>
---
date.c | 29 ++++++++++++++++++++---------
t/t0006-date.sh | 4 ++++
2 files changed, 24 insertions(+), 9 deletions(-)
diff --git a/date.c b/date.c
index 73879d202c..914c733737 100644
--- a/date.c
+++ b/date.c
@@ -1071,13 +1071,22 @@ void datestamp(struct strbuf *out)
/*
* Relative time update (eg "2 days ago"). If we haven't set the time
* yet, we need to set it from current time.
+ *
+ * The tm->tm_mday field has an additional logic of using negative values
+ * for date adjustments: -2 means yesterday and -3 the day before that,
+ * and so on. The idea is to deref such adjustments until we are sure
+ * there's no explicit mday specification in the approxidate string.
*/
static time_t update_tm(struct tm *tm, struct tm *now, time_t sec)
{
time_t n;
- if (tm->tm_mday < 0)
+ if (tm->tm_mday < 0) {
+ int offset = tm->tm_mday + 1;
+ if (sec == 0 && offset < 0)
+ sec = -offset * 24*60*60;
tm->tm_mday = now->tm_mday;
+ }
if (tm->tm_mon < 0)
tm->tm_mon = now->tm_mon;
if (tm->tm_year < 0) {
@@ -1121,44 +1130,46 @@ static void pending_number(struct tm *tm, int *num)
static void date_now(struct tm *tm, struct tm *now, int *num)
{
*num = 0;
+ tm->tm_mday = -1;
update_tm(tm, now, 0);
}
static void date_yesterday(struct tm *tm, struct tm *now, int *num)
{
*num = 0;
+ tm->tm_mday = -1;
update_tm(tm, now, 24*60*60);
}
-static void date_time(struct tm *tm, struct tm *now, int hour)
+static void date_time(struct tm *tm, int hour)
{
/*
* If we do not yet have a specified day, we'll use the most recent
* version of "hour" relative to now. But that may be yesterday.
*/
if (tm->tm_mday < 0 && tm->tm_hour < hour)
- update_tm(tm, now, 24*60*60);
+ tm->tm_mday = -2; /* eventually handled by update_tm() */
tm->tm_hour = hour;
tm->tm_min = 0;
tm->tm_sec = 0;
}
-static void date_midnight(struct tm *tm, struct tm *now, int *num)
+static void date_midnight(struct tm *tm, struct tm *now UNUSED, int *num)
{
pending_number(tm, num);
- date_time(tm, now, 0);
+ date_time(tm, 0);
}
-static void date_noon(struct tm *tm, struct tm *now, int *num)
+static void date_noon(struct tm *tm, struct tm *now UNUSED, int *num)
{
pending_number(tm, num);
- date_time(tm, now, 12);
+ date_time(tm, 12);
}
-static void date_tea(struct tm *tm, struct tm *now, int *num)
+static void date_tea(struct tm *tm, struct tm *now UNUSED, int *num)
{
pending_number(tm, num);
- date_time(tm, now, 17);
+ date_time(tm, 17);
}
static void date_pm(struct tm *tm, struct tm *now UNUSED, int *num)
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index d800cb30fe..432b92f841 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -209,9 +209,13 @@ 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 'today at noon' '2009-08-30 12:00:00' '-12 hours'
+check_approxidate 'noon today' '2009-09-01 12:00:00' '+36 hours'
check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'
check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'
+check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'
+check_approxidate 'tea last saturday' '2009-08-29 17:00:00'
+check_approxidate 'tea last saturday' '2009-08-29 17:00:00' '-12 hours'
check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'
check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'
check_approxidate '10am noon' '2009-08-29 12:00:00'
--
2.30.2
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v4 0/4] approxidate: tweak special date formats
2026-05-14 11:55 ` [PATCH v3 0/4] approxidate: tweak special date formats Tuomas Ahola
` (3 preceding siblings ...)
2026-05-14 11:55 ` [PATCH v3 4/4] approxidate: use deferred mday adjustments for "specials" Tuomas Ahola
@ 2026-05-16 15:15 ` Tuomas Ahola
2026-05-16 15:15 ` [PATCH v4 1/4] approxidate: make "today" wrap to midnight Tuomas Ahola
` (3 more replies)
4 siblings, 4 replies; 28+ messages in thread
From: Tuomas Ahola @ 2026-05-16 15:15 UTC (permalink / raw)
To: git; +Cc: Jeff King, Junio C Hamano, Tuomas Ahola
The approxidate system is an endless source of absurdities. Let's make the
usual "eh, that's crazy, let's do better with this input" type of fix[1], and
tweak some sharp edge cases, including one noticed by Linus back in 2006[2].
After this series, "tea" and "noon" will work predictably with all kinds of
date formats (today, yesterday, last Friday, January 5th, one year ago
yesterday...) regardless of the current time of day.
More importantly, approxidate is taught about "today", meaning by default
the last midnight, as discussed in the RFC thread.[3]
Links:
1. https://lore.kernel.org/git/20181115144854.GB16450@sigill.intra.peff.net/
2. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/
3. https://lore.kernel.org/git/20260515205803.26211-1-taahol@utu.fi/
Tuomas Ahola (4):
approxidate: make "today" wrap to midnight
t0006: add support for approxidate test date adjustment
approxidate: make "specials" respect fixed day-of-month
approxidate: use deferred mday adjustments for "specials"
Documentation/rev-list-options.adoc | 3 +-
date.c | 45 ++++++++++++++++++++++-------
t/t0006-date.sh | 43 ++++++++++++++++++++++++++-
3 files changed, 79 insertions(+), 12 deletions(-)
Intervall-diff mot v3:
-: ---------- > 1: 86bcb70ac2 approxidate: make "today" wrap to midnight
1: 7ea9c9967b = 2: 9863f359a1 t0006: add support for approxidate test date adjustment
2: 3a21727dbe < -: ---------- approxidate: alias "today" to "now"
3: d1992d23d0 = 3: 830de02f74 approxidate: make "specials" respect fixed day-of-month
4: 0b1a10305c ! 4: d33195dc91 approxidate: use deferred mday adjustments for "specials"
@@ Commit message
field for deferred date adjustments which can be easily reverted, so
that the default logic of the special formats only applies if we don't
get any explicit date (mday) specification. In particular, overwrite
- the field with -1 in "now" and "yesterday", so that those formats will
+ the field with -1 in "today" and "yesterday", so that those formats will
be relative to the current date. That makes specifications like "tea
yesterday" behave more sensibly: instead of going backwards to the
last tea-time and then a day back, Git will now understand that as the
@@ date.c: void datestamp(struct strbuf *out)
if (tm->tm_mon < 0)
tm->tm_mon = now->tm_mon;
if (tm->tm_year < 0) {
-@@ date.c: static void pending_number(struct tm *tm, int *num)
- static void date_now(struct tm *tm, struct tm *now, int *num)
- {
- *num = 0;
-+ tm->tm_mday = -1;
- update_tm(tm, now, 0);
- }
-
+@@ date.c: static void date_now(struct tm *tm, struct tm *now, int *num)
static void date_yesterday(struct tm *tm, struct tm *now, int *num)
{
*num = 0;
@@ date.c: static void pending_number(struct tm *tm, int *num)
}
static void date_pm(struct tm *tm, struct tm *now UNUSED, int *num)
+@@ date.c: static void date_today(struct tm *tm, struct tm *now, int *num UNUSED)
+ if (tm->tm_hour == now->tm_hour &&
+ tm->tm_min == now->tm_min &&
+ tm->tm_sec == now->tm_sec)
+- date_time(tm, now, 0);
++ date_time(tm, 0);
++ tm->tm_mday = -1;
+ update_tm(tm, now, 0);
+ }
+
## t/t0006-date.sh ##
@@ t/t0006-date.sh: check_approxidate '3:00' '2009-08-30 03:00:00'
base-commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0
--
2.30.2
^ permalink raw reply [flat|nested] 28+ messages in thread* [PATCH v4 1/4] approxidate: make "today" wrap to midnight
2026-05-16 15:15 ` [PATCH v4 0/4] approxidate: tweak special date formats Tuomas Ahola
@ 2026-05-16 15:15 ` Tuomas Ahola
2026-05-16 15:15 ` [PATCH v4 2/4] t0006: add support for approxidate test date adjustment Tuomas Ahola
` (2 subsequent siblings)
3 siblings, 0 replies; 28+ messages in thread
From: Tuomas Ahola @ 2026-05-16 15:15 UTC (permalink / raw)
To: git; +Cc: Jeff King, Junio C Hamano, Tuomas Ahola
Although some commands do reject invalid approxidate expressions,
in other cases those are simply evaluated as the current time.
Oftentimes that is a perfectly good compromise to handle silly
requests, but it isn't without rough edges.
Because of the silent acceptance, it is easy to forget that
"today" isn't actually a valid approxidate format. That is
a bit awkward because while the fallback logic of using the
current time does make some sense, there is no deliberative
decision behind such behavior of "today". Indeed, whatever
(non-)action "today" currently has, is just an accidental
side effect.
That means "git log --since=today" is currently unlikely to
print anything at all as it tries to list commits dated with
*future* timestamps. Arguably it would be more useful to
list the commits of the current day---i.e. those made since
midnight.
On the other hand, "git log --until=today" doesn't really
filter commits at all. Changing the definition of "today"
would make it return the commits made before the current day.
That isn't without problems though---running "git log
--until=today" in the late afternoon could reasonably include
the work done earlier that day (as the command currently
does do).
Still the utility of no-op "--until=today" is debatable and
perhaps outweighed by the pros of having "--since=today" to
mean "--since=midnight". The thing is that the approxidate
machinery doesn't know about its consumers, so the meaning
of "today" has to be the same for "--since" and "--until".
In fact, "git log --until=" is documented as
`--until=<date>`::
`--before=<date>`::
Show commits older than _<date>_,
so excluding commits made today would actually match the
documentation more closely.
Moreover, a revision parameter "@{today}" is currently outright
rejected. Making "today" a valid approxidate time format could
make a natural way to specify the state of the ref at the start
of the current day.
Bind "today" to new function `date_today()` as an approxidate
special. Make it return the last midnight if no specific time
is given; i.e. retain the old behavior of "noon today" and such.
Document the new behavior of "git log --since=today" in
rev-list-options.adoc.
Signed-off-by: Tuomas Ahola <taahol@utu.fi>
---
Documentation/rev-list-options.adoc | 3 ++-
date.c | 10 ++++++++++
t/t0006-date.sh | 2 ++
3 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc
index 2d195a1474..a5abadf689 100644
--- a/Documentation/rev-list-options.adoc
+++ b/Documentation/rev-list-options.adoc
@@ -23,7 +23,8 @@ ordering and formatting options, such as `--reverse`.
`--since=<date>`::
`--after=<date>`::
- Show commits more recent than _<date>_.
+ Show commits more recent than _<date>_. As a special case,
+ 'today' means the last midnight.
`--since-as-filter=<date>`::
Show all commits more recent than _<date>_. This visits
diff --git a/date.c b/date.c
index 17a95077cf..343d6aab6f 100644
--- a/date.c
+++ b/date.c
@@ -1192,6 +1192,15 @@ static void date_never(struct tm *tm, struct tm *now UNUSED, int *num)
*num = 0;
}
+static void date_today(struct tm *tm, struct tm *now, int *num UNUSED)
+{
+ if (tm->tm_hour == now->tm_hour &&
+ tm->tm_min == now->tm_min &&
+ tm->tm_sec == now->tm_sec)
+ date_time(tm, now, 0);
+ update_tm(tm, now, 0);
+}
+
static const struct special {
const char *name;
void (*fn)(struct tm *, struct tm *, int *);
@@ -1204,6 +1213,7 @@ static const struct special {
{ "AM", date_am },
{ "never", date_never },
{ "now", date_now },
+ { "today", date_today },
{ NULL }
};
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 53ced36df4..07bf6115ab 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -164,6 +164,7 @@ check_approxidate() {
}
check_approxidate now '2009-08-30 19:20:00'
+check_approxidate today '2009-08-30 00:00: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'
@@ -187,6 +188,7 @@ 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 'Jan 5 today' '2009-01-30 00:00:00'
check_approxidate 'Jun 6, 5AM' '2009-06-06 05:00:00'
check_approxidate '5AM Jun 6' '2009-06-06 05:00:00'
--
2.30.2
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v4 2/4] t0006: add support for approxidate test date adjustment
2026-05-16 15:15 ` [PATCH v4 0/4] approxidate: tweak special date formats Tuomas Ahola
2026-05-16 15:15 ` [PATCH v4 1/4] approxidate: make "today" wrap to midnight Tuomas Ahola
@ 2026-05-16 15:15 ` Tuomas Ahola
2026-05-16 15:15 ` [PATCH v4 3/4] approxidate: make "specials" respect fixed day-of-month Tuomas Ahola
2026-05-16 15:15 ` [PATCH v4 4/4] approxidate: use deferred mday adjustments for "specials" Tuomas Ahola
3 siblings, 0 replies; 28+ messages in thread
From: Tuomas Ahola @ 2026-05-16 15:15 UTC (permalink / raw)
To: git; +Cc: Jeff King, Junio C Hamano, Tuomas Ahola
t0006 uses a hard-coded test date and provides no convenient
way to override it temporarily. Add an optional parameter to
check_approxidate to adjust the time as needed, and demonstrate
the feature with a new test.
Signed-off-by: Tuomas Ahola <taahol@utu.fi>
---
t/t0006-date.sh | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 07bf6115ab..15fbc12861 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -155,12 +155,41 @@ check_parse '2100-00-00 00:00:00 -11' bad
check_parse '2100-00-00 00:00:00 +11' bad
REQUIRE_64BIT_TIME=
+add_time_offset() {
+ case "$3" in
+ hours)
+ unit=$(( 60*60 ))
+ ;;
+ days)
+ unit=$(( 24*60*60 ))
+ ;;
+ esac
+ offset=$(( $2 * unit ))
+ echo $(( $1 + offset ))
+}
+
check_approxidate() {
+ old_date=$GIT_TEST_DATE_NOW
+ if test "$3" = "failure"
+ then
+ expection="$3"
+ else
+ expection=${4:-success}
+ offset="$3"
+ fi
+ if test -n "$offset"
+ then
+ GIT_TEST_DATE_NOW=$(add_time_offset $old_date $offset)
+ caption="$1; offset $offset"
+ else
+ caption=$1
+ fi
echo "$1 -> $2 +0000" >expect
- test_expect_${3:-success} "parse approxidate ($1)" "
+ test_expect_$expection "parse approxidate ($caption)" "
test-tool date approxidate '$1' >actual &&
test_cmp expect actual
"
+ GIT_TEST_DATE_NOW=$old_date
}
check_approxidate now '2009-08-30 19:20:00'
@@ -183,6 +212,8 @@ check_approxidate 'noon today' '2009-08-30 12:00:00'
check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'
check_approxidate '10am noon' '2009-08-29 12:00:00'
+check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'
+check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'
check_approxidate 'last tuesday' '2009-08-25 19:20:00'
check_approxidate 'July 5th' '2009-07-05 19:20:00'
--
2.30.2
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v4 3/4] approxidate: make "specials" respect fixed day-of-month
2026-05-16 15:15 ` [PATCH v4 0/4] approxidate: tweak special date formats Tuomas Ahola
2026-05-16 15:15 ` [PATCH v4 1/4] approxidate: make "today" wrap to midnight Tuomas Ahola
2026-05-16 15:15 ` [PATCH v4 2/4] t0006: add support for approxidate test date adjustment Tuomas Ahola
@ 2026-05-16 15:15 ` Tuomas Ahola
2026-05-16 15:15 ` [PATCH v4 4/4] approxidate: use deferred mday adjustments for "specials" Tuomas Ahola
3 siblings, 0 replies; 28+ messages in thread
From: Tuomas Ahola @ 2026-05-16 15:15 UTC (permalink / raw)
To: git; +Cc: Jeff King, Junio C Hamano, Tuomas Ahola
The special approxidate time formats, "noon" and "tea" differ from
"12pm" and "5pm" by having the feature of wrapping to the previous day
if the current time is before those hours:
now -> 2026-05-13 11:00:00 +0000
12pm -> 2026-05-13 12:00:00 +0000
5pm -> 2026-05-13 17:00:00 +0000
noon -> 2026-05-12 12:00:00 +0000
tea -> 2026-05-12 17:00:00 +0000
However, that logic carries too far. Even when the date is specified,
the behavior of the "specials" depends on the current time. Assuming
the same time as above, we get:
today at noon -> 2026-05-12 12:00:00 +0000 (should be 13 May)
13 May at tea -> 2026-05-12 17:00:00 +0000
or, using an example mentioned in date-formats.adoc:
last Friday at noon -> 2026-05-07 12:00:00 +0000 (should be 8 May)
The quirk seems to be rather old. Already in 2006, Linus Torvalds
remarked that the date yielded by "one year ago yesterday at tea-time"
was "just silly and not even correct". Indeed, even today it gives:
One year ago yesterday at tea-time -> 2025-05-11 17:00:00 +0000
(should be 12 May)
Let's fix all of those with a simple patch. Check whether we already
have a specified day-of-month in `tm->tm_mday` and make `date_time()`
stick to it. Ensure the correct behavior with relevant tests.
Links:
1. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/
Signed-off-by: Tuomas Ahola <taahol@utu.fi>
---
date.c | 6 +++++-
t/t0006-date.sh | 4 ++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/date.c b/date.c
index 343d6aab6f..7a458f3cac 100644
--- a/date.c
+++ b/date.c
@@ -1132,7 +1132,11 @@ static void date_yesterday(struct tm *tm, struct tm *now, int *num)
static void date_time(struct tm *tm, struct tm *now, int hour)
{
- if (tm->tm_hour < hour)
+ /*
+ * If we do not yet have a specified day, we'll use the most recent
+ * version of "hour" relative to now. But that may be yesterday.
+ */
+ if (tm->tm_mday < 0 && tm->tm_hour < hour)
update_tm(tm, now, 24*60*60);
tm->tm_hour = hour;
tm->tm_min = 0;
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 15fbc12861..7358903046 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -209,8 +209,12 @@ 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 'today at noon' '2009-08-30 12:00:00' '-12 hours'
check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'
+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'
check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'
+check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'
check_approxidate '10am noon' '2009-08-29 12:00:00'
check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'
check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'
--
2.30.2
^ permalink raw reply related [flat|nested] 28+ messages in thread* [PATCH v4 4/4] approxidate: use deferred mday adjustments for "specials"
2026-05-16 15:15 ` [PATCH v4 0/4] approxidate: tweak special date formats Tuomas Ahola
` (2 preceding siblings ...)
2026-05-16 15:15 ` [PATCH v4 3/4] approxidate: make "specials" respect fixed day-of-month Tuomas Ahola
@ 2026-05-16 15:15 ` Tuomas Ahola
3 siblings, 0 replies; 28+ messages in thread
From: Tuomas Ahola @ 2026-05-16 15:15 UTC (permalink / raw)
To: git; +Cc: Jeff King, Junio C Hamano, Tuomas Ahola
There are cases where the "wrap-to-yesterday" behavior of "tea" and
"noon" should be reverted later on down the line, so that "today tea"
and "tea today" won't yield different results. However, the logic of
approxidate doesn't seem to lend itself particularly well to
such cases.
Start tackling the issue by reusing negative values of `tm->tm_mday`
field for deferred date adjustments which can be easily reverted, so
that the default logic of the special formats only applies if we don't
get any explicit date (mday) specification. In particular, overwrite
the field with -1 in "today" and "yesterday", so that those formats will
be relative to the current date. That makes specifications like "tea
yesterday" behave more sensibly: instead of going backwards to the
last tea-time and then a day back, Git will now understand that as the
tea-time of yesterday.
Replace the call of `update_tm()` in `date_time()` with the assignment
`tm->tm_mday = -2`. Add the corresponding code to handle that in
`update_tm()`, wrapping to the previous day if the field still holds
such assignment, meaning that we haven't seen any better specification
for the day-of-month. On the other hand, `mday=-3` would mean going
two days back and so on. Even though such functionality isn't
actually needed by this patch, it won't add much complexity in the
code and is rather natural way to handle such values.
As `date_time()` won't no longer need the `now` struct, mark the
associated function parameters as unused. The parameters themselves
have to stay, however, as those functions are called through pointers
in `approxidate_alpha`. Add relevant tests to cover the changes.
Signed-off-by: Tuomas Ahola <taahol@utu.fi>
---
date.c | 31 +++++++++++++++++++++----------
t/t0006-date.sh | 4 ++++
2 files changed, 25 insertions(+), 10 deletions(-)
diff --git a/date.c b/date.c
index 7a458f3cac..6e7cf907da 100644
--- a/date.c
+++ b/date.c
@@ -1071,13 +1071,22 @@ void datestamp(struct strbuf *out)
/*
* Relative time update (eg "2 days ago"). If we haven't set the time
* yet, we need to set it from current time.
+ *
+ * The tm->tm_mday field has an additional logic of using negative values
+ * for date adjustments: -2 means yesterday and -3 the day before that,
+ * and so on. The idea is to deref such adjustments until we are sure
+ * there's no explicit mday specification in the approxidate string.
*/
static time_t update_tm(struct tm *tm, struct tm *now, time_t sec)
{
time_t n;
- if (tm->tm_mday < 0)
+ if (tm->tm_mday < 0) {
+ int offset = tm->tm_mday + 1;
+ if (sec == 0 && offset < 0)
+ sec = -offset * 24*60*60;
tm->tm_mday = now->tm_mday;
+ }
if (tm->tm_mon < 0)
tm->tm_mon = now->tm_mon;
if (tm->tm_year < 0) {
@@ -1127,38 +1136,39 @@ static void date_now(struct tm *tm, struct tm *now, int *num)
static void date_yesterday(struct tm *tm, struct tm *now, int *num)
{
*num = 0;
+ tm->tm_mday = -1;
update_tm(tm, now, 24*60*60);
}
-static void date_time(struct tm *tm, struct tm *now, int hour)
+static void date_time(struct tm *tm, int hour)
{
/*
* If we do not yet have a specified day, we'll use the most recent
* version of "hour" relative to now. But that may be yesterday.
*/
if (tm->tm_mday < 0 && tm->tm_hour < hour)
- update_tm(tm, now, 24*60*60);
+ tm->tm_mday = -2; /* eventually handled by update_tm() */
tm->tm_hour = hour;
tm->tm_min = 0;
tm->tm_sec = 0;
}
-static void date_midnight(struct tm *tm, struct tm *now, int *num)
+static void date_midnight(struct tm *tm, struct tm *now UNUSED, int *num)
{
pending_number(tm, num);
- date_time(tm, now, 0);
+ date_time(tm, 0);
}
-static void date_noon(struct tm *tm, struct tm *now, int *num)
+static void date_noon(struct tm *tm, struct tm *now UNUSED, int *num)
{
pending_number(tm, num);
- date_time(tm, now, 12);
+ date_time(tm, 12);
}
-static void date_tea(struct tm *tm, struct tm *now, int *num)
+static void date_tea(struct tm *tm, struct tm *now UNUSED, int *num)
{
pending_number(tm, num);
- date_time(tm, now, 17);
+ date_time(tm, 17);
}
static void date_pm(struct tm *tm, struct tm *now UNUSED, int *num)
@@ -1201,7 +1211,8 @@ static void date_today(struct tm *tm, struct tm *now, int *num UNUSED)
if (tm->tm_hour == now->tm_hour &&
tm->tm_min == now->tm_min &&
tm->tm_sec == now->tm_sec)
- date_time(tm, now, 0);
+ date_time(tm, 0);
+ tm->tm_mday = -1;
update_tm(tm, now, 0);
}
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 7358903046..b187b1bfc4 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -210,9 +210,13 @@ 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 'today at noon' '2009-08-30 12:00:00' '-12 hours'
+check_approxidate 'noon today' '2009-09-01 12:00:00' '+36 hours'
check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'
check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'
+check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'
+check_approxidate 'tea last saturday' '2009-08-29 17:00:00'
+check_approxidate 'tea last saturday' '2009-08-29 17:00:00' '-12 hours'
check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'
check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'
check_approxidate '10am noon' '2009-08-29 12:00:00'
--
2.30.2
^ permalink raw reply related [flat|nested] 28+ messages in thread