Subject: [PATCH v2 2/3] approxidate: make "specials" respect fixed day-of-month

From: Tuomas Ahola <[email-masked]>
Date: Tue, 12 May 2026 17:54:29 +0300

The special approxidate time formats, "noon" and "tea", wrap
to the previous day if the current time is before 12 or 5 pm,
respectively.  That holds even when an actual date is supplied;
therefore, "10 May at tea" and "last Friday at noon" can cause
the date to be set to a seemingly wrong day:

	now -> 2026-05-12 11:00:00 +0000
	10 May at tea -> 2026-05-09 17:00:00 +0000
	last Friday at noon -> 2026-05-07 12:00:00 +0000
	One year ago yesterday at tea-time -> 2025-05-10 17:00:00 +0000

The last example is from Linus Torvalds who remarked in 2006
that the answer was "just silly and not even correct." [1]

As "last Friday at noon" is mentioned in the documentation
(date-formats.adoc) it would be nice if it worked correctly.
Let's fix the glitch with a simple patch.

Check whether we already have a specified (non-negative) mday
and make date_time() stick to it.  Add a suitable time offset
to the relevant test.

While we are at it, add "today" as an alias of "now", so that
"today at noon" will do the right thing, too, and assert that
with a new test.

Links:
  1. https://lore.kernel.org/git/[email-masked]/

Signed-off-by: Tuomas Ahola <[email-masked]>
---
 date.c          | 8 +++++++-
 t/t0006-date.sh | 4 ++++
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/date.c b/date.c
index 17a95077cf..e48cc2a4d7 100644
--- a/date.c
+++ b/date.c
@@ -1132,7 +1132,12 @@ 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)
+	/*
+	 * By default, "tea" and "noon" refer to last such time in the
+	 * past, be it today or yesterday.  With a specified mday,
+	 * that logic is overridden.
+	 */
+	if (tm->tm_mday < 0 && tm->tm_hour < hour)
 		update_tm(tm, now, 24*60*60);
 	tm->tm_hour = hour;
 	tm->tm_min = 0;
@@ -1204,6 +1209,7 @@ static const struct special {
 	{ "AM", date_am },
 	{ "never", date_never },
 	{ "now", date_now },
+	{ "today", date_now },
 	{ NULL }
 };
 
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 5d66267672..e01d093514 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -186,8 +186,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' success -12
 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' success -12
 check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'
+check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' success -12
 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' success +48
-- 
2.30.2