{"thread":{"id":"63156","subject":"[PATCH 2/2] approxidate: overwrite tm_mday for `now` and `yesterday`","startedAt":"2025-03-18T18:17:46Z","lastAt":"2026-05-21T14:14:18Z","messageCount":36,"participants":["Tuomas Ahola","Jeff King","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"514544","messageId":"20250318180201.3653-3-taahol@utu.fi","threadId":"63156","inReplyTo":"20250318180201.3653-1-taahol@utu.fi","subject":"[PATCH 2/2] approxidate: overwrite tm_mday for `now` and `yesterday`","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2025-03-18T18:02:01Z","receivedAt":"2025-03-18T18:17:46Z","isPatch":true,"body":"Date specifications now (or today) and yesterday should refer to\nactual current or previous day.  Especially \"noon today\" or \"noon\nyesterday\" should override the usual logic of using the first previous\nnoon depending on the current time.\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n date.c          | 3 +++\n t/t0006-date.sh | 4 ++--\n 2 files changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/date.c b/date.c\nindex 482a2f8c99..2a8a942d64 100644\n--- a/date.c\n+++ b/date.c\n@@ -1121,12 +1121,14 @@ static void pending_number(struct tm *tm, int *num)\n static void date_now(struct tm *tm, struct tm *now, int *num)\n {\n \t*num = 0;\n+\ttm->tm_mday = -1;\n \tupdate_tm(tm, now, 0);\n }\n \n static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n {\n \t*num = 0;\n+\ttm->tm_mday = -1;\n \tupdate_tm(tm, now, 24*60*60);\n }\n \n@@ -1204,6 +1206,7 @@ static const struct special {\n \t{ \"AM\", date_am },\n \t{ \"never\", date_never },\n \t{ \"now\", date_now },\n+\t{ \"today\", date_now },\n \t{ NULL }\n };\n \ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 5db4b23e0b..6ad931dfb3 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -178,10 +178,10 @@ check_approxidate '6am yesterday' '2009-08-29 06:00:00'\n check_approxidate '6pm yesterday' '2009-08-29 18:00:00'\n check_approxidate '3:00' '2009-08-30 03:00:00'\n check_approxidate '15:00' '2009-08-30 15:00:00'\n-check_approxidate 'noon today' '2009-08-30 12:00:00'\n-check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n (\n \tGIT_TEST_DATE_NOW=$(($GIT_TEST_DATE_NOW-12*60*60)); export GIT_TEST_DATE_NOW\n+\tcheck_approxidate 'noon today' '2009-08-30 12:00:00'\n+\tcheck_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n \tcheck_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n )\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n-- \n2.30.2\n\n"},{"id":"514545","messageId":"20250318180201.3653-1-taahol@utu.fi","threadId":"63156","inReplyTo":null,"subject":"[PATCH 0/2] approxidate: tweak special date formats","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2025-03-18T18:01:59Z","receivedAt":"2025-03-18T18:17:46Z","isPatch":true,"body":"I made a couple of somewhat hacky fixes in approxidate special date\nformats after noticing that some tests succeeded only because of the\nspecified test time which happened to be too late to spot some\nirregularities.\n\nThanks a lot in advance!\n\nTuomas Ahola (2):\n  approxidate: make \"specials\" respect fixed day-of-month\n  approxidate: overwrite tm_mday for `now` and `yesterday`\n\n date.c          | 5 ++++-\n t/t0006-date.sh | 9 ++++++---\n 2 files changed, 10 insertions(+), 4 deletions(-)\n\n\nbase-commit: 683c54c999c301c2cd6f715c411407c413b1d84e\n-- \n2.30.2\n\n"},{"id":"514546","messageId":"20250318180201.3653-2-taahol@utu.fi","threadId":"63156","inReplyTo":"20250318180201.3653-1-taahol@utu.fi","subject":"[PATCH 1/2] approxidate: make \"specials\" respect fixed day-of-month","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2025-03-18T18:02:00Z","receivedAt":"2025-03-18T18:17:46Z","isPatch":true,"body":"The behaviour of noon and tea depends on the current time even when\nthe date is given.  In other words, \"last Friday tea\" is dated to\nThursday if the command is run before 17 pm.\n\nThis can be fixed by checking whether tm->tm_mday already holds a\ndetermined value and tested by setting current time before 12 or 17 pm\nfor noon and tea respectively.\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n date.c          | 2 +-\n t/t0006-date.sh | 5 ++++-\n 2 files changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/date.c b/date.c\nindex 17a95077cf..482a2f8c99 100644\n--- a/date.c\n+++ b/date.c\n@@ -1133,7 +1133,7 @@ static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n static void date_time(struct tm *tm, struct tm *now, int hour)\n {\n \tif (tm->tm_hour < hour)\n-\t\tupdate_tm(tm, now, 24*60*60);\n+\t\tupdate_tm(tm, now, tm->tm_mday < 0 ? 24*60*60 : 0);\n \ttm->tm_hour = hour;\n \ttm->tm_min = 0;\n \ttm->tm_sec = 0;\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 53ced36df4..5db4b23e0b 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -180,7 +180,10 @@ check_approxidate '3:00' '2009-08-30 03:00:00'\n check_approxidate '15:00' '2009-08-30 15:00:00'\n check_approxidate 'noon today' '2009-08-30 12:00:00'\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n-check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n+(\n+\tGIT_TEST_DATE_NOW=$(($GIT_TEST_DATE_NOW-12*60*60)); export GIT_TEST_DATE_NOW\n+\tcheck_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n+)\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n \n check_approxidate 'last tuesday' '2009-08-25 19:20:00'\n-- \n2.30.2\n\n"},{"id":"515639","messageId":"20250404081911.GA762635@coredump.intra.peff.net","threadId":"63156","inReplyTo":"20250318180201.3653-2-taahol@utu.fi","subject":"Re: [PATCH 1/2] approxidate: make \"specials\" respect fixed day-of-month","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-04-04T08:19:11Z","receivedAt":"2025-04-04T08:19:19Z","isPatch":true,"body":"On Tue, Mar 18, 2025 at 08:02:00PM +0200, Tuomas Ahola wrote:\n\n> The behaviour of noon and tea depends on the current time even when\n> the date is given.  In other words, \"last Friday tea\" is dated to\n> Thursday if the command is run before 17 pm.\n\nKnowing what approxidate's code is like, I'm not too surprised we'd have\ncorner cases like this. ;)\n\n> This can be fixed by checking whether tm->tm_mday already holds a\n> determined value and tested by setting current time before 12 or 17 pm\n> for noon and tea respectively.\n\nThat makes sense for \"last Friday tea\", but should \"tea last Friday\" or\n\"noon last Friday\" work, too? I suspect it plays quite badly with\napproxidate's left-to-right parsing, so it might not be worth crossing\nthat bridge.\n\n> --- a/date.c\n> +++ b/date.c\n> @@ -1133,7 +1133,7 @@ static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n>  static void date_time(struct tm *tm, struct tm *now, int hour)\n>  {\n>  \tif (tm->tm_hour < hour)\n> -\t\tupdate_tm(tm, now, 24*60*60);\n> +\t\tupdate_tm(tm, now, tm->tm_mday < 0 ? 24*60*60 : 0);\n>  \ttm->tm_hour = hour;\n>  \ttm->tm_min = 0;\n>  \ttm->tm_sec = 0;\n\nMy reading of that conditional is \"if the time computed so far is before\nthe specified hour, then go back a day to yesterday's version of it\". So\n\"noon\" would be yesterday's noon if it is only 11am.\n\nBut if we already have a date, I'd think we would skip that logic\nentirely. I.e., do we need to call update_tm() at all? Certainly in your\npatch we'd pass 0, which would mean no adjustment. Do we need any other\nparts of update_tm()? It looks like it fills the month, day, and year\nfrom the \"now\" struct if they aren't already set, but presumably they'd\nall be set together (and if they're not, then that raises even more\nquestions about whether just checking tm_mday is correct in your patch).\n\nSo it seems like:\n\n  /*\n   * If we do not yet have a specified day, we'll use the most recent\n   * version of \"hour\" relative to now. But that may be yesterday.\n  */\n  if (tm->tm_mday < 0 && tm->tm_hour < hour)\n\tupdate_tm(tm, now, 24*60*60);\n\nwould be equivalent and is IMHO a bit easier to understand.\n\n> diff --git a/t/t0006-date.sh b/t/t0006-date.sh\n> index 53ced36df4..5db4b23e0b 100755\n> --- a/t/t0006-date.sh\n> +++ b/t/t0006-date.sh\n> @@ -180,7 +180,10 @@ check_approxidate '3:00' '2009-08-30 03:00:00'\n>  check_approxidate '15:00' '2009-08-30 15:00:00'\n>  check_approxidate 'noon today' '2009-08-30 12:00:00'\n>  check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n> -check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n> +(\n> +\tGIT_TEST_DATE_NOW=$(($GIT_TEST_DATE_NOW-12*60*60)); export GIT_TEST_DATE_NOW\n> +\tcheck_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n> +)\n>  check_approxidate '10am noon' '2009-08-29 12:00:00'\n\nI'm glad there's a test, but two comments. One, wouldn't we still want\nto keep the existing test to make sure that we continue to do the right\nthing when the \"now\" hour is after noon?\n\nAnd two, I'm pretty sure this sub-shell will confuse the test harness,\nbecause you're running test_expect_success inside it. So any variable\nupdates won't be seen by the parent shell, and I wouldn't be surprised\nif attempts to exit from \"-i\", etc, are broken.\n\nHmm, yeah, running the test yields this output:\n\n  ok 110 - parse approxidate (noon yesterday)\n  ok 111 - parse approxidate (January 5th noon pm)\n  ok 111 - parse approxidate (10am noon)\n  ok 112 - parse approxidate (last tuesday)\n\nbecause the update of the test counter is lost in the sub-shell.\n\nI don't think there's a trivial solution. You can't do a one-shot\nvariable-set like:\n\n  TEST_DATE_NOW=... check_approxidate ...\n\nbecause the behavior of that construct varies between shells. So you\nhave to either add support for an extra parameter to check_approxidate,\nor just save and restore like:\n\n  old_date=$GIT_TEST_DATE_NOW\n  GIT_TEST_DATE_NOW=$((GIT_TEST_DATE_NOW-12*60*60))\n  check_approxidate ...\n  GIT_TEST_DATE_NOW=$old_date\n\nIt would be nice to add a comment there, too, on what the 12-hour\nparameter means and what we expect. Maybe something like:\n\n  # The \"now\" date is usually at 1900 in the evening. Roll it back to\n  # the morning so that it is before the hour of named times like\n  # \"noon\" and \"tea\".\n\n-Peff\n"},{"id":"515640","messageId":"20250404084048.GB762635@coredump.intra.peff.net","threadId":"63156","inReplyTo":"20250318180201.3653-3-taahol@utu.fi","subject":"Re: [PATCH 2/2] approxidate: overwrite tm_mday for `now` and `yesterday`","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-04-04T08:40:48Z","receivedAt":"2025-04-04T08:40:49Z","isPatch":true,"body":"On Tue, Mar 18, 2025 at 08:02:01PM +0200, Tuomas Ahola wrote:\n\n> Date specifications now (or today) and yesterday should refer to\n> actual current or previous day.  Especially \"noon today\" or \"noon\n> yesterday\" should override the usual logic of using the first previous\n> noon depending on the current time.\n\nOK. So if I understand the issue correctly, it is that saying \"noon\"\nsets the day field of the \"struct tm\", and then \"today\" or \"yesterday\"\nwon't override that, because update_tm() tries not to touch those fields\nif they're already set.\n\nThis presumably works for \"10am yesterday\" because \"10am\" just sets the\ntime, and never the date (though I didn't check).\n\n> diff --git a/date.c b/date.c\n> index 482a2f8c99..2a8a942d64 100644\n> --- a/date.c\n> +++ b/date.c\n> @@ -1121,12 +1121,14 @@ static void pending_number(struct tm *tm, int *num)\n>  static void date_now(struct tm *tm, struct tm *now, int *num)\n>  {\n>  \t*num = 0;\n> +\ttm->tm_mday = -1;\n>  \tupdate_tm(tm, now, 0);\n>  }\n\nSo OK, this approach makes sense: throw out the old date we computed so\nthat update_tm() can use today's.\n\nBut is resetting tm_mday enough? If we previously set tm_mon, then\nupdate_tm() won't override that, but it would need to follow along with\ntm_mday, wouldn't it?\n\nSo if it is the morning of April 2nd, that will work fine, since\nyesterday and today have the same month. And it seems to (this is with\nyour patch):\n\n  $ GIT_TEST_DATE_NOW=$(date --date='2025-04-02 11:00' +%s) \\\n    t/helper/test-tool date approxidate 'noon today'\n  noon today -> 2025-04-02 16:00:00 +0000\n\nbut if we try the same thing on the 1st:\n\n  $ GIT_TEST_DATE_NOW=$(date --date='2025-04-01 11:00' +%s) \\\n    t/helper/test-tool date approxidate 'noon today'\n  noon today -> 2025-03-01 16:00:00 +0000\n\nOops, we went back a month. And presumably the same thing would happen\nwith the year on Jan 1st.\n\nInterestingly, since we are passing \"0\" to update_tm() as its\nadjustment, I think this might be easier to read as just:\n\n  tm->tm_mday = now->tm_mday;\n  tm->tm_mon = now->tm_mon;\n  tm->tm_year = now->tm_year;\n\nBut it's possible that the round-trip through mktime() and localtime_r()\nin update_tm() has some value (I wonder what would happen at 2:30am on a\nday when DST springs forward from 2am to 3am).\n\n>  static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n>  {\n>  \t*num = 0;\n> +\ttm->tm_mday = -1;\n>  \tupdate_tm(tm, now, 24*60*60);\n>  }\n\nOK, same here, except obviously we _do_ need to call update_tm() for its\nadjustment.\n\n> @@ -1204,6 +1206,7 @@ static const struct special {\n>  \t{ \"AM\", date_am },\n>  \t{ \"never\", date_never },\n>  \t{ \"now\", date_now },\n> +\t{ \"today\", date_now },\n>  \t{ NULL }\n>  };\n\nSo before we did not understand \"today\" at all, but just ignored it.\nWhich worked because we try to make everything relative to \"now\" by\ndefault anyway. Adding it as you do here makes sense, since now we are\nusing its ability to \"override\" the date set by things like \"noon\".\n\nIt is a little weird that \"now\" and \"today\" share the same\nimplementation. So notably, \"noon now\" does not override the time\n(ignoring \"noon\" completely). But that is already the case before your\npatch (and I think is just one of those approxidate \"if it hurts, don't\ndo it\" quirks).\n\n> --- a/t/t0006-date.sh\n> +++ b/t/t0006-date.sh\n> @@ -178,10 +178,10 @@ check_approxidate '6am yesterday' '2009-08-29 06:00:00'\n>  check_approxidate '6pm yesterday' '2009-08-29 18:00:00'\n>  check_approxidate '3:00' '2009-08-30 03:00:00'\n>  check_approxidate '15:00' '2009-08-30 15:00:00'\n> -check_approxidate 'noon today' '2009-08-30 12:00:00'\n> -check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n>  (\n>  \tGIT_TEST_DATE_NOW=$(($GIT_TEST_DATE_NOW-12*60*60)); export GIT_TEST_DATE_NOW\n> +\tcheck_approxidate 'noon today' '2009-08-30 12:00:00'\n> +\tcheck_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n>  \tcheck_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n>  )\n\nSame comments on the tests as in patch 1. I think we'd want to add,\nrather than replace, and put these in the same save/restore block rather\nthan using a subshell.\n\n-Peff\n"},{"id":"543183","messageId":"20260512145430.13212-2-taahol@utu.fi","threadId":"63156","inReplyTo":"20260512145430.13212-1-taahol@utu.fi","subject":"[PATCH v2 1/3] t0006: add support for approxidate test date adjustment","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-12T14:54:28Z","receivedAt":"2026-05-12T15:09:47Z","isPatch":true,"body":"t0006 uses a hard-coded test date and provides no convenient\nway to override it temporarily.  Add an optional parameter to\ncheck_approxidate to adjust the time as needed, and demonstrate\nthe feature with a new test.\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n t/t0006-date.sh | 11 ++++++++++-\n 1 file changed, 10 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 53ced36df4..5d66267672 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -156,11 +156,18 @@ check_parse '2100-00-00 00:00:00 +11' bad\n REQUIRE_64BIT_TIME=\n \n check_approxidate() {\n+\told_date=$GIT_TEST_DATE_NOW\n+\tGIT_TEST_DATE_NOW=$(($old_date${4:-+0}*60*60)); export GIT_TEST_DATE_NOW\n+\tcaption=$1\n+\tif [ ! -z $4 ]; then\n+\t\tcaption=\"$caption; offset $4h\"\n+\tfi\n \techo \"$1 -> $2 +0000\" >expect\n-\ttest_expect_${3:-success} \"parse approxidate ($1)\" \"\n+\ttest_expect_${3:-success} \"parse approxidate ($caption)\" \"\n \ttest-tool date approxidate '$1' >actual &&\n \ttest_cmp expect actual\n \t\"\n+\tGIT_TEST_DATE_NOW=$old_date; export GIT_TEST_DATE_NOW\n }\n \n check_approxidate now '2009-08-30 19:20:00'\n@@ -182,6 +189,8 @@ check_approxidate 'noon today' '2009-08-30 12:00:00'\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n+check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'\n+check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' success +48\n \n check_approxidate 'last tuesday' '2009-08-25 19:20:00'\n check_approxidate 'July 5th' '2009-07-05 19:20:00'\n-- \n2.30.2\n\n"},{"id":"543184","messageId":"20260512145430.13212-1-taahol@utu.fi","threadId":"63156","inReplyTo":"20250318180201.3653-1-taahol@utu.fi","subject":"[PATCH v2 0/3] approxidate: tweak special date formats","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-12T14:54:27Z","receivedAt":"2026-05-12T15:09:47Z","isPatch":true,"body":"The approxidate system is an endless source of absurdities.  Let's make the\nusual \"eh, that's crazy, let's do better with this input\" type of fix[1], and\ntweak some sharp edge cases, including one noticed by Linus back in 2006[2].\n\nAfter this series, \"tea\" and \"noon\" will work predictably with all kinds of\ndate formats (today, yesterday, last Friday, January 5th, one year ago\nyesterday...) regardless of the current time of day.\n\nLinks:\n  1. https://lore.kernel.org/git/20181115144854.GB16450@sigill.intra.peff.net/\n  2. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/\n\nTuomas Ahola (3):\n  t0006: add support for approxidate test date adjustment\n  approxidate: make \"specials\" respect fixed day-of-month\n  approxidate: use deferred mday adjustments for \"specials\"\n\n date.c          | 37 +++++++++++++++++++++++++++----------\n t/t0006-date.sh | 19 ++++++++++++++++++-\n 2 files changed, 45 insertions(+), 11 deletions(-)\n\nIntervall-diff mot v1:\n1:  9bfff739fd < -:  ---------- approxidate: make \"specials\" respect fixed day-of-month\n2:  1a4398e5a1 < -:  ---------- approxidate: overwrite tm_mday for `now` and `yesterday`\n-:  ---------- > 1:  118f1825ac t0006: add support for approxidate test date adjustment\n-:  ---------- > 2:  21c4858c47 approxidate: make \"specials\" respect fixed day-of-month\n-:  ---------- > 3:  cf72403102 approxidate: use deferred mday adjustments for \"specials\"\n\nbase-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0\n-- \n2.30.2\n\n"},{"id":"543185","messageId":"20260512145430.13212-3-taahol@utu.fi","threadId":"63156","inReplyTo":"20260512145430.13212-1-taahol@utu.fi","subject":"[PATCH v2 2/3] approxidate: make \"specials\" respect fixed day-of-month","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-12T14:54:29Z","receivedAt":"2026-05-12T15:09:47Z","isPatch":true,"body":"The special approxidate time formats, \"noon\" and \"tea\", wrap\nto the previous day if the current time is before 12 or 5 pm,\nrespectively.  That holds even when an actual date is supplied;\ntherefore, \"10 May at tea\" and \"last Friday at noon\" can cause\nthe date to be set to a seemingly wrong day:\n\n\tnow -> 2026-05-12 11:00:00 +0000\n\t10 May at tea -> 2026-05-09 17:00:00 +0000\n\tlast Friday at noon -> 2026-05-07 12:00:00 +0000\n\tOne year ago yesterday at tea-time -> 2025-05-10 17:00:00 +0000\n\nThe last example is from Linus Torvalds who remarked in 2006\nthat the answer was \"just silly and not even correct.\" [1]\n\nAs \"last Friday at noon\" is mentioned in the documentation\n(date-formats.adoc) it would be nice if it worked correctly.\nLet's fix the glitch with a simple patch.\n\nCheck whether we already have a specified (non-negative) mday\nand make date_time() stick to it.  Add a suitable time offset\nto the relevant test.\n\nWhile we are at it, add \"today\" as an alias of \"now\", so that\n\"today at noon\" will do the right thing, too, and assert that\nwith a new test.\n\nLinks:\n  1. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n date.c          | 8 +++++++-\n t/t0006-date.sh | 4 ++++\n 2 files changed, 11 insertions(+), 1 deletion(-)\n\ndiff --git a/date.c b/date.c\nindex 17a95077cf..e48cc2a4d7 100644\n--- a/date.c\n+++ b/date.c\n@@ -1132,7 +1132,12 @@ static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n \n static void date_time(struct tm *tm, struct tm *now, int hour)\n {\n-\tif (tm->tm_hour < hour)\n+\t/*\n+\t * By default, \"tea\" and \"noon\" refer to last such time in the\n+\t * past, be it today or yesterday.  With a specified mday,\n+\t * that logic is overridden.\n+\t */\n+\tif (tm->tm_mday < 0 && tm->tm_hour < hour)\n \t\tupdate_tm(tm, now, 24*60*60);\n \ttm->tm_hour = hour;\n \ttm->tm_min = 0;\n@@ -1204,6 +1209,7 @@ static const struct special {\n \t{ \"AM\", date_am },\n \t{ \"never\", date_never },\n \t{ \"now\", date_now },\n+\t{ \"today\", date_now },\n \t{ NULL }\n };\n \ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 5d66267672..e01d093514 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -186,8 +186,12 @@ check_approxidate '6pm yesterday' '2009-08-29 18:00:00'\n check_approxidate '3:00' '2009-08-30 03:00:00'\n check_approxidate '15:00' '2009-08-30 15:00:00'\n check_approxidate 'noon today' '2009-08-30 12:00:00'\n+check_approxidate 'today at noon' '2009-08-30 12:00:00' success -12\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' success -12\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n+check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' success -12\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'\n check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' success +48\n-- \n2.30.2\n\n"},{"id":"543186","messageId":"20260512145430.13212-4-taahol@utu.fi","threadId":"63156","inReplyTo":"20260512145430.13212-1-taahol@utu.fi","subject":"[PATCH v2 3/3] approxidate: use deferred mday adjustments for \"specials\"","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-12T14:54:30Z","receivedAt":"2026-05-12T15:09:47Z","isPatch":true,"body":"There are cases where the \"wrap-to-yesterday\" behavior of \"tea\" and\n\"noon\" should be reverted later on down the line, so that \"today tea\"\nand \"tea today\" won't yield different results.  However, the logic of\napproxidate doesn't seem to lend itself particularly well to\nsuch cases.\n\nStart tackling the issue by reusing negative values of `tm->tm_mday`\nfield for deferred date adjustments which can be easily reverted, so\nthat the default logic of the special formats only applies if we don't\nget any explicit date (mday) specification.  In particular, overwrite\nthe field with -1 in \"now\" and \"yesterday\", so that those formats will\nbe relative to the current date.  That makes specifications like \"tea\nyesterday\" behave more sensibly: instead of going backwards to the\nlast tea-time and then a day back, Git will now understand that as the\ntea-time of yesterday.\n\nReplace the call of `update_tm()` in `date_time()` with the assignment\n`tm->tm_mday = -2`.  Add the corresponding code to handle that in\n`update_tm()`, wrapping to the previous day if the field still holds\nsuch assignment, meaning that we haven't seen any better specification\nfor the day-of-month.  On the other hand, `mday=-3` would mean going\ntwo days back and so on.  Even though such functionality isn't\nactually needed by this patch, it won't add much complexity in the\ncode and is rather natural way to handle such values.\n\nAs `date_time()` won't no longer need the `now` struct, mark the\nassociated function parameters as unused.  The parameters themselves\nhave to stay, however, as those functions are called through pointers\nin `approxidate_alpha`.  Add relevant tests to cover the changes.\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n date.c          | 31 +++++++++++++++++++++----------\n t/t0006-date.sh |  4 ++++\n 2 files changed, 25 insertions(+), 10 deletions(-)\n\ndiff --git a/date.c b/date.c\nindex e48cc2a4d7..795f1ac2e9 100644\n--- a/date.c\n+++ b/date.c\n@@ -1071,13 +1071,22 @@ void datestamp(struct strbuf *out)\n /*\n  * Relative time update (eg \"2 days ago\").  If we haven't set the time\n  * yet, we need to set it from current time.\n+ *\n+ * The tm->tm_mday field has an additional logic of using negative values\n+ * for date adjustments: -2 means yesterday and -3 the day before that,\n+ * and so on.  The idea is to deref such adjustments until we are sure\n+ * there's no explicit mday specification in the approxidate string.\n  */\n static time_t update_tm(struct tm *tm, struct tm *now, time_t sec)\n {\n \ttime_t n;\n \n-\tif (tm->tm_mday < 0)\n+\tif (tm->tm_mday < 0) {\n+\t\tint offset = tm->tm_mday + 1;\n+\t\tif (sec == 0 && offset < 0)\n+\t\t\tsec = -offset * 24*60*60;\n \t\ttm->tm_mday = now->tm_mday;\n+\t}\n \tif (tm->tm_mon < 0)\n \t\ttm->tm_mon = now->tm_mon;\n \tif (tm->tm_year < 0) {\n@@ -1121,45 +1130,47 @@ static void pending_number(struct tm *tm, int *num)\n static void date_now(struct tm *tm, struct tm *now, int *num)\n {\n \t*num = 0;\n+\ttm->tm_mday = -1;\n \tupdate_tm(tm, now, 0);\n }\n \n static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n {\n \t*num = 0;\n+\ttm->tm_mday = -1;\n \tupdate_tm(tm, now, 24*60*60);\n }\n \n-static void date_time(struct tm *tm, struct tm *now, int hour)\n+static void date_time(struct tm *tm, int hour)\n {\n \t/*\n \t * By default, \"tea\" and \"noon\" refer to last such time in the\n \t * past, be it today or yesterday.  With a specified mday,\n-\t * that logic is overridden.\n+\t * or e.g. \"noon today\", that logic is overridden.\n \t */\n \tif (tm->tm_mday < 0 && tm->tm_hour < hour)\n-\t\tupdate_tm(tm, now, 24*60*60);\n+\t\ttm->tm_mday = -2; /* eventually handled by update_tm() */\n \ttm->tm_hour = hour;\n \ttm->tm_min = 0;\n \ttm->tm_sec = 0;\n }\n \n-static void date_midnight(struct tm *tm, struct tm *now, int *num)\n+static void date_midnight(struct tm *tm, struct tm *now UNUSED, int *num)\n {\n \tpending_number(tm, num);\n-\tdate_time(tm, now, 0);\n+\tdate_time(tm, 0);\n }\n \n-static void date_noon(struct tm *tm, struct tm *now, int *num)\n+static void date_noon(struct tm *tm, struct tm *now UNUSED, int *num)\n {\n \tpending_number(tm, num);\n-\tdate_time(tm, now, 12);\n+\tdate_time(tm, 12);\n }\n \n-static void date_tea(struct tm *tm, struct tm *now, int *num)\n+static void date_tea(struct tm *tm, struct tm *now UNUSED, int *num)\n {\n \tpending_number(tm, num);\n-\tdate_time(tm, now, 17);\n+\tdate_time(tm, 17);\n }\n \n static void date_pm(struct tm *tm, struct tm *now UNUSED, int *num)\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex e01d093514..2670269bb7 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -187,9 +187,13 @@ check_approxidate '3:00' '2009-08-30 03:00:00'\n check_approxidate '15:00' '2009-08-30 15:00:00'\n check_approxidate 'noon today' '2009-08-30 12:00:00'\n check_approxidate 'today at noon' '2009-08-30 12:00:00' success -12\n+check_approxidate 'noon today' '2009-09-01 12:00:00' success +36\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' success -12\n+check_approxidate 'noon yesterday' '2009-08-29 12:00:00' success -12\n+check_approxidate 'tea last saturday' '2009-08-29 17:00:00'\n+check_approxidate 'tea last saturday' '2009-08-29 17:00:00' success -12\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' success -12\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n-- \n2.30.2\n\n"},{"id":"543193","messageId":"xmqqfr3wlh79.fsf@gitster.g","threadId":"63156","inReplyTo":"20260512145430.13212-2-taahol@utu.fi","subject":"Re: [PATCH v2 1/3] t0006: add support for approxidate test date adjustment","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-05-12T16:34:50Z","receivedAt":"2026-05-12T16:34:54Z","isPatch":true,"body":"Tuomas Ahola <taahol@utu.fi> writes:\n\n>  check_approxidate() {\n> +\told_date=$GIT_TEST_DATE_NOW\n> +\tGIT_TEST_DATE_NOW=$(($old_date${4:-+0}*60*60)); export GIT_TEST_DATE_NOW\n> +\tcaption=$1\n> +\tif [ ! -z $4 ]; then\n> +\t\tcaption=\"$caption; offset $4h\"\n> +\tfi\n\nAs you are not doing the test-date-now adjustment when $4 is not\ngiven, wouldn't it be a lot easier to read if you did something like\n\n\told_date=$GIT_TEST_DATE_NOW\n\tif test -n \"$4\"\n\tthen\n\t\t# the convention for $4 is a bit weird in that it \n\t\t# comes with its own +/- operator in front.\n\t\tGIT_TEST_DATE_NOW=$(( $old_date $4 * 60 * 60 ))\n\t\tcaption=\"$1; offset $4h\"\n\telse\n        \tcaption=$1\n\tfi\n\ninstead?  Other two minor points are\n\n - Documentation/SubmittingPatches prefers an explicit \"test\" over\n   \"[ ... ]\", and have \"then\", \"else\", etc. on their own lines.\n\n - As you never \"unset\" GIT_TEST_DATE_NOW, you do not have to keep\n   exporting it.  It is not like there are two variables (one for\n   shell, the other for environment) and every time you set the\n   shell one you need to export to reflect the value to the\n   environment one.  Rather, a single \"export\" marks a shell\n   variable and every time it changes value, it is updated in the\n   environment as well.\n\n>  \techo \"$1 -> $2 +0000\" >expect\n> -\ttest_expect_${3:-success} \"parse approxidate ($1)\" \"\n> +\ttest_expect_${3:-success} \"parse approxidate ($caption)\" \"\n>  \ttest-tool date approxidate '$1' >actual &&\n>  \ttest_cmp expect actual\n>  \t\"\n> +\tGIT_TEST_DATE_NOW=$old_date; export GIT_TEST_DATE_NOW\n>  }\n>  \n>  check_approxidate now '2009-08-30 19:20:00'\n> @@ -182,6 +189,8 @@ check_approxidate 'noon today' '2009-08-30 12:00:00'\n>  check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n>  check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n>  check_approxidate '10am noon' '2009-08-29 12:00:00'\n> +check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'\n> +check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' success +48\n>  \n>  check_approxidate 'last tuesday' '2009-08-25 19:20:00'\n>  check_approxidate 'July 5th' '2009-07-05 19:20:00'\n"},{"id":"543194","messageId":"xmqq33zwlgec.fsf@gitster.g","threadId":"63156","inReplyTo":"20260512145430.13212-3-taahol@utu.fi","subject":"Re: [PATCH v2 2/3] approxidate: make \"specials\" respect fixed day-of-month","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-05-12T16:52:11Z","receivedAt":"2026-05-12T16:52:14Z","isPatch":true,"body":"Tuomas Ahola <taahol@utu.fi> writes:\n\n> The special approxidate time formats, \"noon\" and \"tea\", wrap\n> to the previous day if the current time is before 12 or 5 pm,\n> respectively.  That holds even when an actual date is supplied;\n> therefore, \"10 May at tea\" and \"last Friday at noon\" can cause\n> the date to be set to a seemingly wrong day:\n>\n> \tnow -> 2026-05-12 11:00:00 +0000\n> \t10 May at tea -> 2026-05-09 17:00:00 +0000\n> \tlast Friday at noon -> 2026-05-07 12:00:00 +0000\n\nIt would help readers to say that Friday of that week was May 8th to\nmake it easier for them to see why this is a wrong answer.\n\n> \tOne year ago yesterday at tea-time -> 2025-05-10 17:00:00 +0000\n>\n> The last example is from Linus Torvalds who remarked in 2006\n> that the answer was \"just silly and not even correct.\" [1]\n\nIt may be just me, but it was hard for me to guess if you are\njustifying how these answers are correct, or you are presenting\nexamples of wrong output.  Perhaps starting the paragraph with cases\nwhere the \"wrap to the previous\" gets right to set the stage may\nmake it easier to understand?  Let's see...\n\n    The ... 'noon' and 'tea', wrap to the ... before 12 or 5 pm,\n    respectively.  So for example if it is 11am on 2026-05-12, then\n\n\tnow  -> 2026-05-12 11:00:00 +0000\n\tnoon -> 2026-05-11 11:00:00 +0000\n\ttea  -> 2026-05-11 17:00:00 +0000\n\n    which would work well when you ask for \"git log --since=tea\",\n    for example.\n\n    That hold even when ... seemingly wrong day:\n\n> As \"last Friday at noon\" is mentioned in the documentation\n> (date-formats.adoc) it would be nice if it worked correctly.\n> Let's fix the glitch with a simple patch.\n>\n> Check whether we already have a specified (non-negative) mday\n> and make date_time() stick to it.  Add a suitable time offset\n> to the relevant test.\n>\n> While we are at it, add \"today\" as an alias of \"now\", so that\n> \"today at noon\" will do the right thing, too, and assert that\n> with a new test.\n>\n> Links:\n>   1. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/\n>\n> Signed-off-by: Tuomas Ahola <taahol@utu.fi>\n> ---\n>  date.c          | 8 +++++++-\n>  t/t0006-date.sh | 4 ++++\n>  2 files changed, 11 insertions(+), 1 deletion(-)\n>\n> diff --git a/date.c b/date.c\n> index 17a95077cf..e48cc2a4d7 100644\n> --- a/date.c\n> +++ b/date.c\n> @@ -1132,7 +1132,12 @@ static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n>  \n>  static void date_time(struct tm *tm, struct tm *now, int hour)\n>  {\n> -\tif (tm->tm_hour < hour)\n> +\t/*\n> +\t * By default, \"tea\" and \"noon\" refer to last such time in the\n> +\t * past, be it today or yesterday.  With a specified mday,\n> +\t * that logic is overridden.\n> +\t */\n\nAgain, this may be just me, but I happen to find the version of\ncomment in Peff's review on the earlier iteration of this series\nmuch easier to understand.\n\n> +\tif (tm->tm_mday < 0 && tm->tm_hour < hour)\n>  \t\tupdate_tm(tm, now, 24*60*60);\n>  \ttm->tm_hour = hour;\n>  \ttm->tm_min = 0;\n> @@ -1204,6 +1209,7 @@ static const struct special {\n>  \t{ \"AM\", date_am },\n>  \t{ \"never\", date_never },\n>  \t{ \"now\", date_now },\n> +\t{ \"today\", date_now },\n\nHmph, this may not work very well for \"git log --since=today\", which\nyou may want to stop immediately when the traversal reaches a patch\nwritten before the most recent midnight, instead of stopping without\ngiving anything back.\n\n>  \t{ NULL }\n>  };\n>  \n> diff --git a/t/t0006-date.sh b/t/t0006-date.sh\n> index 5d66267672..e01d093514 100755\n> --- a/t/t0006-date.sh\n> +++ b/t/t0006-date.sh\n> @@ -186,8 +186,12 @@ check_approxidate '6pm yesterday' '2009-08-29 18:00:00'\n>  check_approxidate '3:00' '2009-08-30 03:00:00'\n>  check_approxidate '15:00' '2009-08-30 15:00:00'\n>  check_approxidate 'noon today' '2009-08-30 12:00:00'\n> +check_approxidate 'today at noon' '2009-08-30 12:00:00' success -12\n>  check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n> +check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n> +check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' success -12\n>  check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n> +check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' success -12\n>  check_approxidate '10am noon' '2009-08-29 12:00:00'\n>  check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'\n>  check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' success +48\n"},{"id":"543209","messageId":"20260512183519.GA70851@coredump.intra.peff.net","threadId":"63156","inReplyTo":"20260512145430.13212-2-taahol@utu.fi","subject":"Re: [PATCH v2 1/3] t0006: add support for approxidate test date adjustment","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-05-12T18:35:19Z","receivedAt":"2026-05-12T18:35:27Z","isPatch":true,"body":"On Tue, May 12, 2026 at 05:54:28PM +0300, Tuomas Ahola wrote:\n\n> t0006 uses a hard-coded test date and provides no convenient\n> way to override it temporarily.  Add an optional parameter to\n> check_approxidate to adjust the time as needed, and demonstrate\n> the feature with a new test.\n\nMakes sense, but two small-ish comments:\n\n> +check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' success +48\n\nOne, it sucks to have to say \"success\" here, but is awkward because now\nwe have two optional arguments. There's nobody passing \"failure\" right\nnow, so we could just drop support, though that might be annoying later\nwhen somebody wants to add a failing test. But we could perhaps switch\nto allowing:\n\n  check_approxidate --failure 'January 5th yesterday' ...etc\n\nwhich is fairly natural.\n\nThis is something we've run into in many different test scripts, and I\nthink the harness could do a better job of supporting this. Perhaps with\nsomething like:\n\n  test_expect() {\n\tif \"$GIT_TEST_EXPECT\" = \"fail\"\n\tthen\n\t\ttest_expect_failure \"$@\"\n\telse\n\t\ttest_expect_success \"$@\"\n  }\n\n  test_fails() {\n\t# probably needs to be more careful of one-shot with functions\n\tGIT_TEST_EXPECT=fail \"$@\"\n  }\n\n  # this is a normal passing test snippet\n  test_expect 'foo' 'git foo'\n\n  # this one expects failure, but it can be toggled easily by\n  # removing the leading \"test_fails\" wrapper. Not much better\n  # than swapping out s/failure/success/ now. But...\n  test_fails test_expect 'bar' 'git bar'\n\n  # this one just does the right thing if the helper function\n  # is using test_expect under the hood\n  test_fails check_approxidate ...\n\nI dunno. It is probably too big a rabbit hole to do before your series,\nso mostly I'm just thinking out loud.\n\n> +check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' success +48\n\nThe second thing is that \"+48\" is pretty opaque. It's a relative offset\nto some arbitrary point. To some degree the script already suffers from\nthat (all of the tests are using some arbitrary point), but I think the\noffset (without units!) adds a layer of indirection that makes it even\nmore confusing.\n\nI wonder how hard it would be to just take an arbitrary time instead,\nand then you could write:\n\n  check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '2009-08-28 12:00:00'\n\nor whatever. There is a chicken-and-egg problem with testing our date\nroutines and using the date routines to parse out the starting point.\nBut I think for approxidate, we could be using the strict parser (tested\nseparately earlier via check_parse) to handle the base time.\n\n-Peff\n"},{"id":"543297","messageId":"20260514115520.6660-3-taahol@utu.fi","threadId":"63156","inReplyTo":"20260514115520.6660-1-taahol@utu.fi","subject":"[PATCH v3 2/4] approxidate: alias \"today\" to \"now\"","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-14T11:55:18Z","receivedAt":"2026-05-14T11:57:17Z","isPatch":true,"body":"As far as approxidate in concerned, \"today\" is a no-op.  That makes\nit functionally equivalent to \"now\" in commands like\n\n        $ git log --since=today\n\nMake that behavior explicit by binding \"today\" to `date_now()`.\nThat way later patches can give \"today\" some functionality in\ncases like \"today at noon\".\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n\nNotes:\n    > Hmph, this may not work very well for \"git log --since=today\", which\n    > you may want to stop immediately when the traversal reaches a patch\n    > written before the most recent midnight, instead of stopping without\n    > giving anything back.\n    >\n    \n    Sorry, I don't know if I understood.  Does the patch change the behavior of\n    that command somehow?  Is there some kind of edge case I missed?\n    \n    That said, if we do want to change it so that \"git log --since=today\"\n    worked like \"--since=midnight\", this seems to do the trick:\n    \n    ```\n    static void date_today(struct tm *tm, struct tm *now, int *num)\n    {\n    \tif (tm->tm_hour == now->tm_hour &&\n    \t    tm->tm_min == now->tm_min &&\n    \t    tm->tm_sec == now->tm_sec)\n    \t\tdate_midnight(tm, now, num);\n    \tdate_now(tm, now, num);\n    }\n    ```\n\n date.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/date.c b/date.c\nindex 17a95077cf..412aca6dc4 100644\n--- a/date.c\n+++ b/date.c\n@@ -1204,6 +1204,7 @@ static const struct special {\n \t{ \"AM\", date_am },\n \t{ \"never\", date_never },\n \t{ \"now\", date_now },\n+\t{ \"today\", date_now },\n \t{ NULL }\n };\n \n-- \n2.30.2\n\n"},{"id":"543298","messageId":"20260514115520.6660-2-taahol@utu.fi","threadId":"63156","inReplyTo":"20260514115520.6660-1-taahol@utu.fi","subject":"[PATCH v3 1/4] t0006: add support for approxidate test date adjustment","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-14T11:55:17Z","receivedAt":"2026-05-14T11:57:17Z","isPatch":true,"body":"t0006 uses a hard-coded test date and provides no convenient\nway to override it temporarily.  Add an optional parameter to\ncheck_approxidate to adjust the time as needed, and demonstrate\nthe feature with a new test.\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n\nNotes:\n    > As you are not doing the test-date-now adjustment when $4 is not\n    > given, wouldn't it be a lot easier to read if you did something like\n    >\n    > \told_date=$GIT_TEST_DATE_NOW\n    > \tif test -n \"$4\"\n    > \tthen\n    > \t\t# the convention for $4 is a bit weird in that it\n    > \t\t# comes with its own +/- operator in front.\n    > \t\tGIT_TEST_DATE_NOW=$(( $old_date $4 * 60 * 60 ))\n    > \t\tcaption=\"$1; offset $4h\"\n    > \telse\n    >         \tcaption=$1\n    > \tfi\n    >\n    > instead?  Other two minor points are\n    >\n    >  - Documentation/SubmittingPatches prefers an explicit \"test\" over\n    >    \"[ ... ]\", and have \"then\", \"else\", etc. on their own lines.\n    >\n    >  - As you never \"unset\" GIT_TEST_DATE_NOW, you do not have to keep\n    >    exporting it.  It is not like there are two variables (one for\n    >    shell, the other for environment) and every time you set the\n    >    shell one you need to export to reflect the value to the\n    >    environment one.  Rather, a single \"export\" marks a shell\n    >    variable and every time it changes value, it is updated in the\n    >    environment as well.\n    >\n    \n    Thanks, applied.\n    \n    > One, it sucks to have to say \"success\" here, but is awkward because now\n    > we have two optional arguments. There's nobody passing \"failure\" right\n    > now, so we could just drop support, though that might be annoying later\n    > when somebody wants to add a failing test. But we could perhaps switch\n    > to allowing:\n    >\n    \n    Ok, now it works without \"success\" in between, and \"failure\" works without\n    an offset, too.\n    \n    \tcheck_approxidate <test-string> <expected-result> [<test-time-offset>] [failure]\n    \n    > The second thing is that \"+48\" is pretty opaque. It's a relative offset\n    > to some arbitrary point. To some degree the script already suffers from\n    > that (all of the tests are using some arbitrary point), but I think the\n    > offset (without units!) adds a layer of indirection that makes it even\n    > more confusing.\n    >\n    \n    Good catch.  Now it is at least marginally better with the added units.\n\n t/t0006-date.sh | 33 ++++++++++++++++++++++++++++++++-\n 1 file changed, 32 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 53ced36df4..c7667bade2 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -155,12 +155,41 @@ check_parse '2100-00-00 00:00:00 -11' bad\n check_parse '2100-00-00 00:00:00 +11' bad\n REQUIRE_64BIT_TIME=\n \n+add_time_offset() {\n+\tcase \"$3\" in\n+\thours)\n+\t\tunit=$(( 60*60 ))\n+\t\t;;\n+\tdays)\n+\t\tunit=$(( 24*60*60 ))\n+\t\t;;\n+\tesac\n+\toffset=$(( $2 * unit ))\n+\techo $(( $1 + offset ))\n+}\n+\n check_approxidate() {\n+\told_date=$GIT_TEST_DATE_NOW\n+\tif test \"$3\" = \"failure\"\n+\tthen\n+\t\texpection=\"$3\"\n+\telse\n+\t\texpection=${4:-success}\n+\t\toffset=\"$3\"\n+\tfi\n+\tif test -n \"$offset\"\n+\tthen\n+\t\tGIT_TEST_DATE_NOW=$(add_time_offset $old_date $offset)\n+\t\tcaption=\"$1; offset $offset\"\n+\telse\n+\t\tcaption=$1\n+\tfi\n \techo \"$1 -> $2 +0000\" >expect\n-\ttest_expect_${3:-success} \"parse approxidate ($1)\" \"\n+\ttest_expect_$expection \"parse approxidate ($caption)\" \"\n \ttest-tool date approxidate '$1' >actual &&\n \ttest_cmp expect actual\n \t\"\n+\tGIT_TEST_DATE_NOW=$old_date\n }\n \n check_approxidate now '2009-08-30 19:20:00'\n@@ -182,6 +211,8 @@ check_approxidate 'noon today' '2009-08-30 12:00:00'\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n+check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'\n+check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'\n \n check_approxidate 'last tuesday' '2009-08-25 19:20:00'\n check_approxidate 'July 5th' '2009-07-05 19:20:00'\n-- \n2.30.2\n\n"},{"id":"543299","messageId":"20260514115520.6660-1-taahol@utu.fi","threadId":"63156","inReplyTo":"20260512145430.13212-1-taahol@utu.fi","subject":"[PATCH v3 0/4] approxidate: tweak special date formats","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-14T11:55:16Z","receivedAt":"2026-05-14T11:57:17Z","isPatch":true,"body":"The approxidate system is an endless source of absurdities.  Let's make the\nusual \"eh, that's crazy, let's do better with this input\" type of fix[1], and\ntweak some sharp edge cases, including one noticed by Linus back in 2006[2].\n\nAfter this series, \"tea\" and \"noon\" will work predictably with all kinds of\ndate formats (today, yesterday, last Friday, January 5th, one year ago\nyesterday...) regardless of the current time of day.\n\nLinks:\n  1. https://lore.kernel.org/git/20181115144854.GB16450@sigill.intra.peff.net/\n  2. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/\n\nTuomas Ahola (4):\n  t0006: add support for approxidate test date adjustment\n  approxidate: alias \"today\" to \"now\"\n  approxidate: make \"specials\" respect fixed day-of-month\n  approxidate: use deferred mday adjustments for \"specials\"\n\n date.c          | 36 ++++++++++++++++++++++++++----------\n t/t0006-date.sh | 41 ++++++++++++++++++++++++++++++++++++++++-\n 2 files changed, 66 insertions(+), 11 deletions(-)\n\nIntervall-diff mot v2:\n1:  118f1825ac < -:  ---------- t0006: add support for approxidate test date adjustment\n2:  21c4858c47 < -:  ---------- approxidate: make \"specials\" respect fixed day-of-month\n-:  ---------- > 1:  7ea9c9967b t0006: add support for approxidate test date adjustment\n-:  ---------- > 2:  3a21727dbe approxidate: alias \"today\" to \"now\"\n-:  ---------- > 3:  d1992d23d0 approxidate: make \"specials\" respect fixed day-of-month\n3:  cf72403102 ! 4:  0b1a10305c approxidate: use deferred mday adjustments for \"specials\"\n    @@ date.c: static void pending_number(struct tm *tm, int *num)\n     +static void date_time(struct tm *tm, int hour)\n      {\n      \t/*\n    - \t * By default, \"tea\" and \"noon\" refer to last such time in the\n    - \t * past, be it today or yesterday.  With a specified mday,\n    --\t * that logic is overridden.\n    -+\t * or e.g. \"noon today\", that logic is overridden.\n    + \t * If we do not yet have a specified day, we'll use the most recent\n    + \t * version of \"hour\" relative to now.  But that may be yesterday.\n      \t */\n      \tif (tm->tm_mday < 0 && tm->tm_hour < hour)\n     -\t\tupdate_tm(tm, now, 24*60*60);\n    @@ t/t0006-date.sh\n     @@ t/t0006-date.sh: check_approxidate '3:00' '2009-08-30 03:00:00'\n      check_approxidate '15:00' '2009-08-30 15:00:00'\n      check_approxidate 'noon today' '2009-08-30 12:00:00'\n    - check_approxidate 'today at noon' '2009-08-30 12:00:00' success -12\n    -+check_approxidate 'noon today' '2009-09-01 12:00:00' success +36\n    + check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'\n    ++check_approxidate 'noon today' '2009-09-01 12:00:00' '+36 hours'\n      check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n      check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n    - check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' success -12\n    -+check_approxidate 'noon yesterday' '2009-08-29 12:00:00' success -12\n    + check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'\n    ++check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'\n     +check_approxidate 'tea last saturday' '2009-08-29 17:00:00'\n    -+check_approxidate 'tea last saturday' '2009-08-29 17:00:00' success -12\n    ++check_approxidate 'tea last saturday' '2009-08-29 17:00:00' '-12 hours'\n      check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n    - check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' success -12\n    + check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'\n      check_approxidate '10am noon' '2009-08-29 12:00:00'\n\nbase-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0\n-- \n2.30.2\n\n"},{"id":"543300","messageId":"20260514115520.6660-4-taahol@utu.fi","threadId":"63156","inReplyTo":"20260514115520.6660-1-taahol@utu.fi","subject":"[PATCH v3 3/4] approxidate: make \"specials\" respect fixed day-of-month","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-14T11:55:19Z","receivedAt":"2026-05-14T11:57:17Z","isPatch":true,"body":"The special approxidate time formats, \"noon\" and \"tea\" differ from\n\"12pm\" and \"5pm\" by having the feature of wrapping to the previous day\nif the current time is before those hours:\n\n\tnow  -> 2026-05-13 11:00:00 +0000\n\n\t12pm -> 2026-05-13 12:00:00 +0000\n\t5pm  -> 2026-05-13 17:00:00 +0000\n\n\tnoon -> 2026-05-12 12:00:00 +0000\n\ttea  -> 2026-05-12 17:00:00 +0000\n\nHowever, that logic carries too far.  Even when the date is specified,\nthe behavior of the \"specials\" depends on the current time.  Assuming\nthe same time as above, we get:\n\n\ttoday at noon -> 2026-05-12 12:00:00 +0000 (should be 13 May)\n\t13 May at tea -> 2026-05-12 17:00:00 +0000\n\nor, using an example mentioned in date-formats.adoc:\n\n\tlast Friday at noon -> 2026-05-07 12:00:00 +0000 (should be 8 May)\n\nThe quirk seems to be rather old.  Already in 2006, Linus Torvalds\nremarked that the date yielded by \"one year ago yesterday at tea-time\"\nwas \"just silly and not even correct\".  Indeed, even today it gives:\n\n\tOne year ago yesterday at tea-time -> 2025-05-11 17:00:00 +0000\n\t  (should be 12 May)\n\nLet's fix all of those with a simple patch.  Check whether we already\nhave a specified day-of-month in `tm->tm_mday` and make `date_time()`\nstick to it.  Ensure the correct behavior with relevant tests.\n\nLinks:\n  1. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n\nNotes:\n    > Again, this may be just me, but I happen to find the version of\n    > comment in Peff's review on the earlier iteration of this series\n    > much easier to understand.\n    >\n    \n    Thanks, applied.\n\n date.c          | 6 +++++-\n t/t0006-date.sh | 4 ++++\n 2 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/date.c b/date.c\nindex 412aca6dc4..73879d202c 100644\n--- a/date.c\n+++ b/date.c\n@@ -1132,7 +1132,11 @@ static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n \n static void date_time(struct tm *tm, struct tm *now, int hour)\n {\n-\tif (tm->tm_hour < hour)\n+\t/*\n+\t * If we do not yet have a specified day, we'll use the most recent\n+\t * version of \"hour\" relative to now.  But that may be yesterday.\n+\t */\n+\tif (tm->tm_mday < 0 && tm->tm_hour < hour)\n \t\tupdate_tm(tm, now, 24*60*60);\n \ttm->tm_hour = hour;\n \ttm->tm_min = 0;\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex c7667bade2..d800cb30fe 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -208,8 +208,12 @@ check_approxidate '6pm yesterday' '2009-08-29 18:00:00'\n check_approxidate '3:00' '2009-08-30 03:00:00'\n check_approxidate '15:00' '2009-08-30 15:00:00'\n check_approxidate 'noon today' '2009-08-30 12:00:00'\n+check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n+check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'\n check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'\n-- \n2.30.2\n\n"},{"id":"543301","messageId":"20260514115520.6660-5-taahol@utu.fi","threadId":"63156","inReplyTo":"20260514115520.6660-1-taahol@utu.fi","subject":"[PATCH v3 4/4] approxidate: use deferred mday adjustments for \"specials\"","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-14T11:55:20Z","receivedAt":"2026-05-14T11:57:17Z","isPatch":true,"body":"There are cases where the \"wrap-to-yesterday\" behavior of \"tea\" and\n\"noon\" should be reverted later on down the line, so that \"today tea\"\nand \"tea today\" won't yield different results.  However, the logic of\napproxidate doesn't seem to lend itself particularly well to\nsuch cases.\n\nStart tackling the issue by reusing negative values of `tm->tm_mday`\nfield for deferred date adjustments which can be easily reverted, so\nthat the default logic of the special formats only applies if we don't\nget any explicit date (mday) specification.  In particular, overwrite\nthe field with -1 in \"now\" and \"yesterday\", so that those formats will\nbe relative to the current date.  That makes specifications like \"tea\nyesterday\" behave more sensibly: instead of going backwards to the\nlast tea-time and then a day back, Git will now understand that as the\ntea-time of yesterday.\n\nReplace the call of `update_tm()` in `date_time()` with the assignment\n`tm->tm_mday = -2`.  Add the corresponding code to handle that in\n`update_tm()`, wrapping to the previous day if the field still holds\nsuch assignment, meaning that we haven't seen any better specification\nfor the day-of-month.  On the other hand, `mday=-3` would mean going\ntwo days back and so on.  Even though such functionality isn't\nactually needed by this patch, it won't add much complexity in the\ncode and is rather natural way to handle such values.\n\nAs `date_time()` won't no longer need the `now` struct, mark the\nassociated function parameters as unused.  The parameters themselves\nhave to stay, however, as those functions are called through pointers\nin `approxidate_alpha`.  Add relevant tests to cover the changes.\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n date.c          | 29 ++++++++++++++++++++---------\n t/t0006-date.sh |  4 ++++\n 2 files changed, 24 insertions(+), 9 deletions(-)\n\ndiff --git a/date.c b/date.c\nindex 73879d202c..914c733737 100644\n--- a/date.c\n+++ b/date.c\n@@ -1071,13 +1071,22 @@ void datestamp(struct strbuf *out)\n /*\n  * Relative time update (eg \"2 days ago\").  If we haven't set the time\n  * yet, we need to set it from current time.\n+ *\n+ * The tm->tm_mday field has an additional logic of using negative values\n+ * for date adjustments: -2 means yesterday and -3 the day before that,\n+ * and so on.  The idea is to deref such adjustments until we are sure\n+ * there's no explicit mday specification in the approxidate string.\n  */\n static time_t update_tm(struct tm *tm, struct tm *now, time_t sec)\n {\n \ttime_t n;\n \n-\tif (tm->tm_mday < 0)\n+\tif (tm->tm_mday < 0) {\n+\t\tint offset = tm->tm_mday + 1;\n+\t\tif (sec == 0 && offset < 0)\n+\t\t\tsec = -offset * 24*60*60;\n \t\ttm->tm_mday = now->tm_mday;\n+\t}\n \tif (tm->tm_mon < 0)\n \t\ttm->tm_mon = now->tm_mon;\n \tif (tm->tm_year < 0) {\n@@ -1121,44 +1130,46 @@ static void pending_number(struct tm *tm, int *num)\n static void date_now(struct tm *tm, struct tm *now, int *num)\n {\n \t*num = 0;\n+\ttm->tm_mday = -1;\n \tupdate_tm(tm, now, 0);\n }\n \n static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n {\n \t*num = 0;\n+\ttm->tm_mday = -1;\n \tupdate_tm(tm, now, 24*60*60);\n }\n \n-static void date_time(struct tm *tm, struct tm *now, int hour)\n+static void date_time(struct tm *tm, int hour)\n {\n \t/*\n \t * If we do not yet have a specified day, we'll use the most recent\n \t * version of \"hour\" relative to now.  But that may be yesterday.\n \t */\n \tif (tm->tm_mday < 0 && tm->tm_hour < hour)\n-\t\tupdate_tm(tm, now, 24*60*60);\n+\t\ttm->tm_mday = -2; /* eventually handled by update_tm() */\n \ttm->tm_hour = hour;\n \ttm->tm_min = 0;\n \ttm->tm_sec = 0;\n }\n \n-static void date_midnight(struct tm *tm, struct tm *now, int *num)\n+static void date_midnight(struct tm *tm, struct tm *now UNUSED, int *num)\n {\n \tpending_number(tm, num);\n-\tdate_time(tm, now, 0);\n+\tdate_time(tm, 0);\n }\n \n-static void date_noon(struct tm *tm, struct tm *now, int *num)\n+static void date_noon(struct tm *tm, struct tm *now UNUSED, int *num)\n {\n \tpending_number(tm, num);\n-\tdate_time(tm, now, 12);\n+\tdate_time(tm, 12);\n }\n \n-static void date_tea(struct tm *tm, struct tm *now, int *num)\n+static void date_tea(struct tm *tm, struct tm *now UNUSED, int *num)\n {\n \tpending_number(tm, num);\n-\tdate_time(tm, now, 17);\n+\tdate_time(tm, 17);\n }\n \n static void date_pm(struct tm *tm, struct tm *now UNUSED, int *num)\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex d800cb30fe..432b92f841 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -209,9 +209,13 @@ check_approxidate '3:00' '2009-08-30 03:00:00'\n check_approxidate '15:00' '2009-08-30 15:00:00'\n check_approxidate 'noon today' '2009-08-30 12:00:00'\n check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'\n+check_approxidate 'noon today' '2009-09-01 12:00:00' '+36 hours'\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'\n+check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'\n+check_approxidate 'tea last saturday' '2009-08-29 17:00:00'\n+check_approxidate 'tea last saturday' '2009-08-29 17:00:00' '-12 hours'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n-- \n2.30.2\n\n"},{"id":"543329","messageId":"xmqqwlx6f1fo.fsf@gitster.g","threadId":"63156","inReplyTo":"20260514115520.6660-3-taahol@utu.fi","subject":"Re: [PATCH v3 2/4] approxidate: alias \"today\" to \"now\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-05-14T15:36:27Z","receivedAt":"2026-05-14T15:36:29Z","isPatch":true,"body":"Tuomas Ahola <taahol@utu.fi> writes:\n\n>     Sorry, I don't know if I understood.  Does the patch change the behavior of\n>     that command somehow?  Is there some kind of edge case I missed?\n\nNo, I did not think it was a good idea to carve the behaviour in\nstone that \"git log --since=today\" behaves as if it were given \"git\nlog --since=now\".  My reaction would have been very different if we\nwere deliberatly and explicitly saying \"today is synonym for now\",\nbut the thing is, it is not a designed behaviour but what\napproxidate does for anything it does not understand, e.g.\n\n    git log --since=decay\n    git log --since=bogus\n\nall behave as if it were given --since=now.\n"},{"id":"543335","messageId":"xmqqse7uf01o.fsf@gitster.g","threadId":"63156","inReplyTo":"20260514115520.6660-4-taahol@utu.fi","subject":"Re: [PATCH v3 3/4] approxidate: make \"specials\" respect fixed day-of-month","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-05-14T16:06:27Z","receivedAt":"2026-05-14T16:06:30Z","isPatch":true,"body":"Tuomas Ahola <taahol@utu.fi> writes:\n\n> The special approxidate time formats, \"noon\" and \"tea\" differ from\n> \"12pm\" and \"5pm\" by having the feature of wrapping to the previous day\n> if the current time is before those hours:\n>\n> \tnow  -> 2026-05-13 11:00:00 +0000\n>\n> \t12pm -> 2026-05-13 12:00:00 +0000\n> \t5pm  -> 2026-05-13 17:00:00 +0000\n>\n> \tnoon -> 2026-05-12 12:00:00 +0000\n> \ttea  -> 2026-05-12 17:00:00 +0000\n>\n> However, that logic carries too far.  Even when the date is specified,\n> the behavior of the \"specials\" depends on the current time.  Assuming\n> the same time as above, we get:\n>\n> \ttoday at noon -> 2026-05-12 12:00:00 +0000 (should be 13 May)\n> \t13 May at tea -> 2026-05-12 17:00:00 +0000\n>\n> or, using an example mentioned in date-formats.adoc:\n>\n> \tlast Friday at noon -> 2026-05-07 12:00:00 +0000 (should be 8 May)\n>\n> The quirk seems to be rather old.  Already in 2006, Linus Torvalds\n> remarked that the date yielded by \"one year ago yesterday at tea-time\"\n> was \"just silly and not even correct\".  Indeed, even today it gives:\n>\n> \tOne year ago yesterday at tea-time -> 2025-05-11 17:00:00 +0000\n> \t  (should be 12 May)\n>\n> Let's fix all of those with a simple patch.  Check whether we already\n> have a specified day-of-month in `tm->tm_mday` and make `date_time()`\n> stick to it.  Ensure the correct behavior with relevant tests.\n\n\nI find this vastly easier to follow the reasoning, compared to the\nprevious iteration.  Very nicely done.\n\n\n\n>\n> Links:\n>   1. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/\n>\n> Signed-off-by: Tuomas Ahola <taahol@utu.fi>\n> ---\n>\n> Notes:\n>     > Again, this may be just me, but I happen to find the version of\n>     > comment in Peff's review on the earlier iteration of this series\n>     > much easier to understand.\n>     >\n>     \n>     Thanks, applied.\n>\n>  date.c          | 6 +++++-\n>  t/t0006-date.sh | 4 ++++\n>  2 files changed, 9 insertions(+), 1 deletion(-)\n>\n> diff --git a/date.c b/date.c\n> index 412aca6dc4..73879d202c 100644\n> --- a/date.c\n> +++ b/date.c\n> @@ -1132,7 +1132,11 @@ static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n>  \n>  static void date_time(struct tm *tm, struct tm *now, int hour)\n>  {\n> -\tif (tm->tm_hour < hour)\n> +\t/*\n> +\t * If we do not yet have a specified day, we'll use the most recent\n> +\t * version of \"hour\" relative to now.  But that may be yesterday.\n> +\t */\n> +\tif (tm->tm_mday < 0 && tm->tm_hour < hour)\n>  \t\tupdate_tm(tm, now, 24*60*60);\n>  \ttm->tm_hour = hour;\n>  \ttm->tm_min = 0;\n> diff --git a/t/t0006-date.sh b/t/t0006-date.sh\n> index c7667bade2..d800cb30fe 100755\n> --- a/t/t0006-date.sh\n> +++ b/t/t0006-date.sh\n> @@ -208,8 +208,12 @@ check_approxidate '6pm yesterday' '2009-08-29 18:00:00'\n>  check_approxidate '3:00' '2009-08-30 03:00:00'\n>  check_approxidate '15:00' '2009-08-30 15:00:00'\n>  check_approxidate 'noon today' '2009-08-30 12:00:00'\n> +check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'\n>  check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n> +check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n> +check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'\n>  check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n> +check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'\n>  check_approxidate '10am noon' '2009-08-29 12:00:00'\n>  check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'\n>  check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'\n"},{"id":"543361","messageId":"20260514210742.Yc6NZ%taahol@utu.fi","threadId":"63156","inReplyTo":"xmqqwlx6f1fo.fsf@gitster.g","subject":"Re: [PATCH v3 2/4] approxidate: alias \"today\" to \"now\"","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-14T21:07:42Z","receivedAt":"2026-05-14T21:07:52Z","isPatch":true,"body":"Junio C Hamano <gitster@pobox.com> wrote:\n\n> Tuomas Ahola <taahol@utu.fi> writes:\n> \n> >     Sorry, I don't know if I understood.  Does the patch change the behavior of\n> >     that command somehow?  Is there some kind of edge case I missed?\n> \n> No, I did not think it was a good idea to carve the behaviour in\n> stone that \"git log --since=today\" behaves as if it were given \"git\n> log --since=now\".  My reaction would have been very different if we\n> were deliberatly and explicitly saying \"today is synonym for now\",\n> but the thing is, it is not a designed behaviour but what\n> approxidate does for anything it does not understand, e.g.\n> \n>     git log --since=decay\n>     git log --since=bogus\n> \n> all behave as if it were given --since=now.\n\nThanks for spelling that out.  So, as there is no deliberative\ndecision behind the current behaviour of \"today\", the code has\nto remain non-committed on that; we are not at liberty to codify\nthe status quo.  Right?  But perhaps we can find a least common\ndenominator.  \"Today\" means the current day (well, obviously),\nand this seems to be enough to get \"today at noon\" and such to work:\n\n```\nstatic void date_today(struct tm *tm, struct tm *now, int *num)\n{\n\t*num = 0;\n\ttm->tm_mday = now->tm_mday;\n}\n```\n\nIt gets the job done whitout setting in stone too much anything\nthat is up for debate.  Do you see that could work?\n\n--Tuomas\n"},{"id":"543366","messageId":"xmqqik8pea39.fsf@gitster.g","threadId":"63156","inReplyTo":"20260514210742.Yc6NZ%taahol@utu.fi","subject":"Re: [PATCH v3 2/4] approxidate: alias \"today\" to \"now\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-05-15T01:27:06Z","receivedAt":"2026-05-15T01:27:09Z","isPatch":true,"body":"Tuomas Ahola <taahol@utu.fi> writes:\n\n> Junio C Hamano <gitster@pobox.com> wrote:\n>\n>> Tuomas Ahola <taahol@utu.fi> writes:\n>> \n>> >     Sorry, I don't know if I understood.  Does the patch change the behavior of\n>> >     that command somehow?  Is there some kind of edge case I missed?\n>> \n>> No, I did not think it was a good idea to carve the behaviour in\n>> stone that \"git log --since=today\" behaves as if it were given \"git\n>> log --since=now\".  My reaction would have been very different if we\n>> were deliberatly and explicitly saying \"today is synonym for now\",\n>> but the thing is, it is not a designed behaviour but what\n>> approxidate does for anything it does not understand, e.g.\n>> \n>>     git log --since=decay\n>>     git log --since=bogus\n>> \n>> all behave as if it were given --since=now.\n>\n> Thanks for spelling that out.  So, as there is no deliberative\n> decision behind the current behaviour of \"today\", the code has\n> to remain non-committed on that; we are not at liberty to codify\n> the status quo.  Right?\n\nNot right.  It is more like \"Even though we try not to change\nexisting behavoiur left and right without a good reason to avoid\nbreaking existing users' expectations, we should be able to \"fix\"\nwhat is not intended behaviour but is something the code happened to\nbe doing, especially if the current behaviour does not make sense.\n"},{"id":"543368","messageId":"xmqqa4u1e9k9.fsf@gitster.g","threadId":"63156","inReplyTo":"xmqqik8pea39.fsf@gitster.g","subject":"Re: [PATCH v3 2/4] approxidate: alias \"today\" to \"now\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-05-15T01:38:30Z","receivedAt":"2026-05-15T01:38:32Z","isPatch":true,"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Tuomas Ahola <taahol@utu.fi> writes:\n>\n>> Junio C Hamano <gitster@pobox.com> wrote:\n>>\n>>> Tuomas Ahola <taahol@utu.fi> writes:\n>>> \n>>> >     Sorry, I don't know if I understood.  Does the patch change the behavior of\n>>> >     that command somehow?  Is there some kind of edge case I missed?\n>>> \n>>> No, I did not think it was a good idea to carve the behaviour in\n>>> stone that \"git log --since=today\" behaves as if it were given \"git\n>>> log --since=now\".  My reaction would have been very different if we\n>>> were deliberatly and explicitly saying \"today is synonym for now\",\n>>> but the thing is, it is not a designed behaviour but what\n>>> approxidate does for anything it does not understand, e.g.\n>>> \n>>>     git log --since=decay\n>>>     git log --since=bogus\n>>> \n>>> all behave as if it were given --since=now.\n>>\n>> Thanks for spelling that out.  So, as there is no deliberative\n>> decision behind the current behaviour of \"today\", the code has\n>> to remain non-committed on that; we are not at liberty to codify\n>> the status quo.  Right?\n>\n> Not right.  It is more like \"Even though we try not to change\n> existing behavoiur left and right without a good reason to avoid\n> breaking existing users' expectations, we should be able to \"fix\"\n> what is not intended behaviour but is something the code happened to\n> be doing, especially if the current behaviour does not make sense.\n\nAnd the other half of the discussion is that once we explicitly say\n\"today means right now\" and make it official, it makes it much harder\nto fix it later.  So we need to be very careful in our first attempt.\n"},{"id":"543377","messageId":"20260515050243.daahz%taahol@utu.fi","threadId":"63156","inReplyTo":"xmqqa4u1e9k9.fsf@gitster.g","subject":"Re: [PATCH v3 2/4] approxidate: alias \"today\" to \"now\"","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-15T05:02:43Z","receivedAt":"2026-05-15T05:02:52Z","isPatch":true,"body":"Junio C Hamano <gitster@pobox.com> wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > Tuomas Ahola <taahol@utu.fi> writes:\n> >\n> >> Junio C Hamano <gitster@pobox.com> wrote:\n> >>\n> >>> Tuomas Ahola <taahol@utu.fi> writes:\n> >>> \n> >>> >     Sorry, I don't know if I understood.  Does the patch change the behavior of\n> >>> >     that command somehow?  Is there some kind of edge case I missed?\n> >>> \n> >>> No, I did not think it was a good idea to carve the behaviour in\n> >>> stone that \"git log --since=today\" behaves as if it were given \"git\n> >>> log --since=now\".  My reaction would have been very different if we\n> >>> were deliberatly and explicitly saying \"today is synonym for now\",\n> >>> but the thing is, it is not a designed behaviour but what\n> >>> approxidate does for anything it does not understand, e.g.\n> >>> \n> >>>     git log --since=decay\n> >>>     git log --since=bogus\n> >>> \n> >>> all behave as if it were given --since=now.\n> >>\n> >> Thanks for spelling that out.  So, as there is no deliberative\n> >> decision behind the current behaviour of \"today\", the code has\n> >> to remain non-committed on that; we are not at liberty to codify\n> >> the status quo.  Right?\n> >\n> > Not right.  It is more like \"Even though we try not to change\n> > existing behavoiur left and right without a good reason to avoid\n> > breaking existing users' expectations, we should be able to \"fix\"\n> > what is not intended behaviour but is something the code happened to\n> > be doing, especially if the current behaviour does not make sense.\n\nWell, that's not far off from what I wrote.  I just meant we cannot\nmake the current (somewhat accidental) behaviour official *just because*\nit happens to be the status quo.\n\n> \n> And the other half of the discussion is that once we explicitly say \"today\n> means right now\" and make it official, it makes it much harder to fix it\n> later.  So we need to be very careful in our first attempt.\n\nRoger that.\n"},{"id":"543463","messageId":"20260516151540.9611-5-taahol@utu.fi","threadId":"63156","inReplyTo":"20260516151540.9611-1-taahol@utu.fi","subject":"[PATCH v4 4/4] approxidate: use deferred mday adjustments for \"specials\"","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-16T15:15:40Z","receivedAt":"2026-05-16T15:15:48Z","isPatch":true,"body":"There are cases where the \"wrap-to-yesterday\" behavior of \"tea\" and\n\"noon\" should be reverted later on down the line, so that \"today tea\"\nand \"tea today\" won't yield different results.  However, the logic of\napproxidate doesn't seem to lend itself particularly well to\nsuch cases.\n\nStart tackling the issue by reusing negative values of `tm->tm_mday`\nfield for deferred date adjustments which can be easily reverted, so\nthat the default logic of the special formats only applies if we don't\nget any explicit date (mday) specification.  In particular, overwrite\nthe field with -1 in \"today\" and \"yesterday\", so that those formats will\nbe relative to the current date.  That makes specifications like \"tea\nyesterday\" behave more sensibly: instead of going backwards to the\nlast tea-time and then a day back, Git will now understand that as the\ntea-time of yesterday.\n\nReplace the call of `update_tm()` in `date_time()` with the assignment\n`tm->tm_mday = -2`.  Add the corresponding code to handle that in\n`update_tm()`, wrapping to the previous day if the field still holds\nsuch assignment, meaning that we haven't seen any better specification\nfor the day-of-month.  On the other hand, `mday=-3` would mean going\ntwo days back and so on.  Even though such functionality isn't\nactually needed by this patch, it won't add much complexity in the\ncode and is rather natural way to handle such values.\n\nAs `date_time()` won't no longer need the `now` struct, mark the\nassociated function parameters as unused.  The parameters themselves\nhave to stay, however, as those functions are called through pointers\nin `approxidate_alpha`.  Add relevant tests to cover the changes.\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n date.c          | 31 +++++++++++++++++++++----------\n t/t0006-date.sh |  4 ++++\n 2 files changed, 25 insertions(+), 10 deletions(-)\n\ndiff --git a/date.c b/date.c\nindex 7a458f3cac..6e7cf907da 100644\n--- a/date.c\n+++ b/date.c\n@@ -1071,13 +1071,22 @@ void datestamp(struct strbuf *out)\n /*\n  * Relative time update (eg \"2 days ago\").  If we haven't set the time\n  * yet, we need to set it from current time.\n+ *\n+ * The tm->tm_mday field has an additional logic of using negative values\n+ * for date adjustments: -2 means yesterday and -3 the day before that,\n+ * and so on.  The idea is to deref such adjustments until we are sure\n+ * there's no explicit mday specification in the approxidate string.\n  */\n static time_t update_tm(struct tm *tm, struct tm *now, time_t sec)\n {\n \ttime_t n;\n \n-\tif (tm->tm_mday < 0)\n+\tif (tm->tm_mday < 0) {\n+\t\tint offset = tm->tm_mday + 1;\n+\t\tif (sec == 0 && offset < 0)\n+\t\t\tsec = -offset * 24*60*60;\n \t\ttm->tm_mday = now->tm_mday;\n+\t}\n \tif (tm->tm_mon < 0)\n \t\ttm->tm_mon = now->tm_mon;\n \tif (tm->tm_year < 0) {\n@@ -1127,38 +1136,39 @@ static void date_now(struct tm *tm, struct tm *now, int *num)\n static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n {\n \t*num = 0;\n+\ttm->tm_mday = -1;\n \tupdate_tm(tm, now, 24*60*60);\n }\n \n-static void date_time(struct tm *tm, struct tm *now, int hour)\n+static void date_time(struct tm *tm, int hour)\n {\n \t/*\n \t * If we do not yet have a specified day, we'll use the most recent\n \t * version of \"hour\" relative to now.  But that may be yesterday.\n \t */\n \tif (tm->tm_mday < 0 && tm->tm_hour < hour)\n-\t\tupdate_tm(tm, now, 24*60*60);\n+\t\ttm->tm_mday = -2; /* eventually handled by update_tm() */\n \ttm->tm_hour = hour;\n \ttm->tm_min = 0;\n \ttm->tm_sec = 0;\n }\n \n-static void date_midnight(struct tm *tm, struct tm *now, int *num)\n+static void date_midnight(struct tm *tm, struct tm *now UNUSED, int *num)\n {\n \tpending_number(tm, num);\n-\tdate_time(tm, now, 0);\n+\tdate_time(tm, 0);\n }\n \n-static void date_noon(struct tm *tm, struct tm *now, int *num)\n+static void date_noon(struct tm *tm, struct tm *now UNUSED, int *num)\n {\n \tpending_number(tm, num);\n-\tdate_time(tm, now, 12);\n+\tdate_time(tm, 12);\n }\n \n-static void date_tea(struct tm *tm, struct tm *now, int *num)\n+static void date_tea(struct tm *tm, struct tm *now UNUSED, int *num)\n {\n \tpending_number(tm, num);\n-\tdate_time(tm, now, 17);\n+\tdate_time(tm, 17);\n }\n \n static void date_pm(struct tm *tm, struct tm *now UNUSED, int *num)\n@@ -1201,7 +1211,8 @@ static void date_today(struct tm *tm, struct tm *now, int *num UNUSED)\n \tif (tm->tm_hour == now->tm_hour &&\n \t    tm->tm_min == now->tm_min &&\n \t    tm->tm_sec == now->tm_sec)\n-\t\tdate_time(tm, now, 0);\n+\t\tdate_time(tm, 0);\n+\ttm->tm_mday = -1;\n \tupdate_tm(tm, now, 0);\n }\n \ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 7358903046..b187b1bfc4 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -210,9 +210,13 @@ check_approxidate '3:00' '2009-08-30 03:00:00'\n check_approxidate '15:00' '2009-08-30 15:00:00'\n check_approxidate 'noon today' '2009-08-30 12:00:00'\n check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'\n+check_approxidate 'noon today' '2009-09-01 12:00:00' '+36 hours'\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'\n+check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'\n+check_approxidate 'tea last saturday' '2009-08-29 17:00:00'\n+check_approxidate 'tea last saturday' '2009-08-29 17:00:00' '-12 hours'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n-- \n2.30.2\n\n"},{"id":"543461","messageId":"20260516151540.9611-3-taahol@utu.fi","threadId":"63156","inReplyTo":"20260516151540.9611-1-taahol@utu.fi","subject":"[PATCH v4 2/4] t0006: add support for approxidate test date adjustment","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-16T15:15:38Z","receivedAt":"2026-05-16T15:15:49Z","isPatch":true,"body":"t0006 uses a hard-coded test date and provides no convenient\nway to override it temporarily.  Add an optional parameter to\ncheck_approxidate to adjust the time as needed, and demonstrate\nthe feature with a new test.\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n t/t0006-date.sh | 33 ++++++++++++++++++++++++++++++++-\n 1 file changed, 32 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 07bf6115ab..15fbc12861 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -155,12 +155,41 @@ check_parse '2100-00-00 00:00:00 -11' bad\n check_parse '2100-00-00 00:00:00 +11' bad\n REQUIRE_64BIT_TIME=\n \n+add_time_offset() {\n+\tcase \"$3\" in\n+\thours)\n+\t\tunit=$(( 60*60 ))\n+\t\t;;\n+\tdays)\n+\t\tunit=$(( 24*60*60 ))\n+\t\t;;\n+\tesac\n+\toffset=$(( $2 * unit ))\n+\techo $(( $1 + offset ))\n+}\n+\n check_approxidate() {\n+\told_date=$GIT_TEST_DATE_NOW\n+\tif test \"$3\" = \"failure\"\n+\tthen\n+\t\texpection=\"$3\"\n+\telse\n+\t\texpection=${4:-success}\n+\t\toffset=\"$3\"\n+\tfi\n+\tif test -n \"$offset\"\n+\tthen\n+\t\tGIT_TEST_DATE_NOW=$(add_time_offset $old_date $offset)\n+\t\tcaption=\"$1; offset $offset\"\n+\telse\n+\t\tcaption=$1\n+\tfi\n \techo \"$1 -> $2 +0000\" >expect\n-\ttest_expect_${3:-success} \"parse approxidate ($1)\" \"\n+\ttest_expect_$expection \"parse approxidate ($caption)\" \"\n \ttest-tool date approxidate '$1' >actual &&\n \ttest_cmp expect actual\n \t\"\n+\tGIT_TEST_DATE_NOW=$old_date\n }\n \n check_approxidate now '2009-08-30 19:20:00'\n@@ -183,6 +212,8 @@ check_approxidate 'noon today' '2009-08-30 12:00:00'\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n+check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'\n+check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'\n \n check_approxidate 'last tuesday' '2009-08-25 19:20:00'\n check_approxidate 'July 5th' '2009-07-05 19:20:00'\n-- \n2.30.2\n\n"},{"id":"543464","messageId":"20260516151540.9611-1-taahol@utu.fi","threadId":"63156","inReplyTo":"20260514115520.6660-1-taahol@utu.fi","subject":"[PATCH v4 0/4] approxidate: tweak special date formats","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-16T15:15:36Z","receivedAt":"2026-05-16T15:15:49Z","isPatch":true,"body":"The approxidate system is an endless source of absurdities.  Let's make the\nusual \"eh, that's crazy, let's do better with this input\" type of fix[1], and\ntweak some sharp edge cases, including one noticed by Linus back in 2006[2].\n\nAfter this series, \"tea\" and \"noon\" will work predictably with all kinds of\ndate formats (today, yesterday, last Friday, January 5th, one year ago\nyesterday...) regardless of the current time of day.\n\nMore importantly, approxidate is taught about \"today\", meaning by default\nthe last midnight, as discussed in the RFC thread.[3]\n\nLinks:\n  1. https://lore.kernel.org/git/20181115144854.GB16450@sigill.intra.peff.net/\n  2. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/\n  3. https://lore.kernel.org/git/20260515205803.26211-1-taahol@utu.fi/\n\nTuomas Ahola (4):\n  approxidate: make \"today\" wrap to midnight\n  t0006: add support for approxidate test date adjustment\n  approxidate: make \"specials\" respect fixed day-of-month\n  approxidate: use deferred mday adjustments for \"specials\"\n\n Documentation/rev-list-options.adoc |  3 +-\n date.c                              | 45 ++++++++++++++++++++++-------\n t/t0006-date.sh                     | 43 ++++++++++++++++++++++++++-\n 3 files changed, 79 insertions(+), 12 deletions(-)\n\nIntervall-diff mot v3:\n-:  ---------- > 1:  86bcb70ac2 approxidate: make \"today\" wrap to midnight\n1:  7ea9c9967b = 2:  9863f359a1 t0006: add support for approxidate test date adjustment\n2:  3a21727dbe < -:  ---------- approxidate: alias \"today\" to \"now\"\n3:  d1992d23d0 = 3:  830de02f74 approxidate: make \"specials\" respect fixed day-of-month\n4:  0b1a10305c ! 4:  d33195dc91 approxidate: use deferred mday adjustments for \"specials\"\n    @@ Commit message\n         field for deferred date adjustments which can be easily reverted, so\n         that the default logic of the special formats only applies if we don't\n         get any explicit date (mday) specification.  In particular, overwrite\n    -    the field with -1 in \"now\" and \"yesterday\", so that those formats will\n    +    the field with -1 in \"today\" and \"yesterday\", so that those formats will\n         be relative to the current date.  That makes specifications like \"tea\n         yesterday\" behave more sensibly: instead of going backwards to the\n         last tea-time and then a day back, Git will now understand that as the\n    @@ date.c: void datestamp(struct strbuf *out)\n      \tif (tm->tm_mon < 0)\n      \t\ttm->tm_mon = now->tm_mon;\n      \tif (tm->tm_year < 0) {\n    -@@ date.c: static void pending_number(struct tm *tm, int *num)\n    - static void date_now(struct tm *tm, struct tm *now, int *num)\n    - {\n    - \t*num = 0;\n    -+\ttm->tm_mday = -1;\n    - \tupdate_tm(tm, now, 0);\n    - }\n    - \n    +@@ date.c: static void date_now(struct tm *tm, struct tm *now, int *num)\n      static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n      {\n      \t*num = 0;\n    @@ date.c: static void pending_number(struct tm *tm, int *num)\n      }\n      \n      static void date_pm(struct tm *tm, struct tm *now UNUSED, int *num)\n    +@@ date.c: static void date_today(struct tm *tm, struct tm *now, int *num UNUSED)\n    + \tif (tm->tm_hour == now->tm_hour &&\n    + \t    tm->tm_min == now->tm_min &&\n    + \t    tm->tm_sec == now->tm_sec)\n    +-\t\tdate_time(tm, now, 0);\n    ++\t\tdate_time(tm, 0);\n    ++\ttm->tm_mday = -1;\n    + \tupdate_tm(tm, now, 0);\n    + }\n    + \n     \n      ## t/t0006-date.sh ##\n     @@ t/t0006-date.sh: check_approxidate '3:00' '2009-08-30 03:00:00'\n\nbase-commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0\n-- \n2.30.2\n\n"},{"id":"543465","messageId":"20260516151540.9611-2-taahol@utu.fi","threadId":"63156","inReplyTo":"20260516151540.9611-1-taahol@utu.fi","subject":"[PATCH v4 1/4] approxidate: make \"today\" wrap to midnight","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-16T15:15:37Z","receivedAt":"2026-05-16T15:15:49Z","isPatch":true,"body":"Although some commands do reject invalid approxidate expressions,\nin other cases those are simply evaluated as the current time.\nOftentimes that is a perfectly good compromise to handle silly\nrequests, but it isn't without rough edges.\n\nBecause of the silent acceptance, it is easy to forget that\n\"today\" isn't actually a valid approxidate format.  That is\na bit awkward because while the fallback logic of using the\ncurrent time does make some sense, there is no deliberative\ndecision behind such behavior of \"today\".  Indeed, whatever\n(non-)action \"today\" currently has, is just an accidental\nside effect.\n\nThat means \"git log --since=today\" is currently unlikely to\nprint anything at all as it tries to list commits dated with\n*future* timestamps.  Arguably it would be more useful to\nlist the commits of the current day---i.e. those made since\nmidnight.\n\nOn the other hand, \"git log --until=today\" doesn't really\nfilter commits at all.  Changing the definition of \"today\"\nwould make it return the commits made before the current day.\nThat isn't without problems though---running \"git log\n--until=today\" in the late afternoon could reasonably include\nthe work done earlier that day (as the command currently\ndoes do).\n\nStill the utility of no-op \"--until=today\" is debatable and\nperhaps outweighed by the pros of having \"--since=today\" to\nmean \"--since=midnight\".  The thing is that the approxidate\nmachinery doesn't know about its consumers, so the meaning\nof \"today\" has to be the same for \"--since\" and \"--until\".\n\nIn fact, \"git log --until=\" is documented as\n\n\t`--until=<date>`::\n\t`--before=<date>`::\n\t\tShow commits older than _<date>_,\n\nso excluding commits made today would actually match the\ndocumentation more closely.\n\nMoreover, a revision parameter \"@{today}\" is currently outright\nrejected.  Making \"today\" a valid approxidate time format could\nmake a natural way to specify the state of the ref at the start\nof the current day.\n\nBind \"today\" to new function `date_today()` as an approxidate\nspecial.  Make it return the last midnight if no specific time\nis given; i.e. retain the old behavior of \"noon today\" and such.\n\nDocument the new behavior of \"git log --since=today\" in\nrev-list-options.adoc.\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n Documentation/rev-list-options.adoc |  3 ++-\n date.c                              | 10 ++++++++++\n t/t0006-date.sh                     |  2 ++\n 3 files changed, 14 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc\nindex 2d195a1474..a5abadf689 100644\n--- a/Documentation/rev-list-options.adoc\n+++ b/Documentation/rev-list-options.adoc\n@@ -23,7 +23,8 @@ ordering and formatting options, such as `--reverse`.\n \n `--since=<date>`::\n `--after=<date>`::\n-\tShow commits more recent than _<date>_.\n+\tShow commits more recent than _<date>_.  As a special case,\n+\t'today' means the last midnight.\n \n `--since-as-filter=<date>`::\n \tShow all commits more recent than _<date>_. This visits\ndiff --git a/date.c b/date.c\nindex 17a95077cf..343d6aab6f 100644\n--- a/date.c\n+++ b/date.c\n@@ -1192,6 +1192,15 @@ static void date_never(struct tm *tm, struct tm *now UNUSED, int *num)\n \t*num = 0;\n }\n \n+static void date_today(struct tm *tm, struct tm *now, int *num UNUSED)\n+{\n+\tif (tm->tm_hour == now->tm_hour &&\n+\t    tm->tm_min == now->tm_min &&\n+\t    tm->tm_sec == now->tm_sec)\n+\t\tdate_time(tm, now, 0);\n+\tupdate_tm(tm, now, 0);\n+}\n+\n static const struct special {\n \tconst char *name;\n \tvoid (*fn)(struct tm *, struct tm *, int *);\n@@ -1204,6 +1213,7 @@ static const struct special {\n \t{ \"AM\", date_am },\n \t{ \"never\", date_never },\n \t{ \"now\", date_now },\n+\t{ \"today\", date_today },\n \t{ NULL }\n };\n \ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 53ced36df4..07bf6115ab 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -164,6 +164,7 @@ check_approxidate() {\n }\n \n check_approxidate now '2009-08-30 19:20:00'\n+check_approxidate today '2009-08-30 00:00:00'\n check_approxidate '5 seconds ago' '2009-08-30 19:19:55'\n check_approxidate 5.seconds.ago '2009-08-30 19:19:55'\n check_approxidate 10.minutes.ago '2009-08-30 19:10:00'\n@@ -187,6 +188,7 @@ check_approxidate 'last tuesday' '2009-08-25 19:20:00'\n check_approxidate 'July 5th' '2009-07-05 19:20:00'\n check_approxidate '06/05/2009' '2009-06-05 19:20:00'\n check_approxidate '06.05.2009' '2009-05-06 19:20:00'\n+check_approxidate 'Jan 5 today' '2009-01-30 00:00:00'\n \n check_approxidate 'Jun 6, 5AM' '2009-06-06 05:00:00'\n check_approxidate '5AM Jun 6' '2009-06-06 05:00:00'\n-- \n2.30.2\n\n"},{"id":"543462","messageId":"20260516151540.9611-4-taahol@utu.fi","threadId":"63156","inReplyTo":"20260516151540.9611-1-taahol@utu.fi","subject":"[PATCH v4 3/4] approxidate: make \"specials\" respect fixed day-of-month","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-16T15:15:39Z","receivedAt":"2026-05-16T15:15:50Z","isPatch":true,"body":"The special approxidate time formats, \"noon\" and \"tea\" differ from\n\"12pm\" and \"5pm\" by having the feature of wrapping to the previous day\nif the current time is before those hours:\n\n\tnow  -> 2026-05-13 11:00:00 +0000\n\n\t12pm -> 2026-05-13 12:00:00 +0000\n\t5pm  -> 2026-05-13 17:00:00 +0000\n\n\tnoon -> 2026-05-12 12:00:00 +0000\n\ttea  -> 2026-05-12 17:00:00 +0000\n\nHowever, that logic carries too far.  Even when the date is specified,\nthe behavior of the \"specials\" depends on the current time.  Assuming\nthe same time as above, we get:\n\n\ttoday at noon -> 2026-05-12 12:00:00 +0000 (should be 13 May)\n\t13 May at tea -> 2026-05-12 17:00:00 +0000\n\nor, using an example mentioned in date-formats.adoc:\n\n\tlast Friday at noon -> 2026-05-07 12:00:00 +0000 (should be 8 May)\n\nThe quirk seems to be rather old.  Already in 2006, Linus Torvalds\nremarked that the date yielded by \"one year ago yesterday at tea-time\"\nwas \"just silly and not even correct\".  Indeed, even today it gives:\n\n\tOne year ago yesterday at tea-time -> 2025-05-11 17:00:00 +0000\n\t  (should be 12 May)\n\nLet's fix all of those with a simple patch.  Check whether we already\nhave a specified day-of-month in `tm->tm_mday` and make `date_time()`\nstick to it.  Ensure the correct behavior with relevant tests.\n\nLinks:\n  1. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n date.c          | 6 +++++-\n t/t0006-date.sh | 4 ++++\n 2 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/date.c b/date.c\nindex 343d6aab6f..7a458f3cac 100644\n--- a/date.c\n+++ b/date.c\n@@ -1132,7 +1132,11 @@ static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n \n static void date_time(struct tm *tm, struct tm *now, int hour)\n {\n-\tif (tm->tm_hour < hour)\n+\t/*\n+\t * If we do not yet have a specified day, we'll use the most recent\n+\t * version of \"hour\" relative to now.  But that may be yesterday.\n+\t */\n+\tif (tm->tm_mday < 0 && tm->tm_hour < hour)\n \t\tupdate_tm(tm, now, 24*60*60);\n \ttm->tm_hour = hour;\n \ttm->tm_min = 0;\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 15fbc12861..7358903046 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -209,8 +209,12 @@ check_approxidate '6pm yesterday' '2009-08-29 18:00:00'\n check_approxidate '3:00' '2009-08-30 03:00:00'\n check_approxidate '15:00' '2009-08-30 15:00:00'\n check_approxidate 'noon today' '2009-08-30 12:00:00'\n+check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n+check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'\n check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'\n-- \n2.30.2\n\n"},{"id":"543498","messageId":"20260518024007.18689-1-taahol@utu.fi","threadId":"63156","inReplyTo":"20260516151540.9611-5-taahol@utu.fi","subject":"[PATCH] fixup! approxidate: use deferred mday adjustments for \"specials\"","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-18T02:40:07Z","receivedAt":"2026-05-18T02:40:38Z","isPatch":true,"body":"Oops, let's reorder these lines.\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n t/t0006-date.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex b187b1bfc4..63e5628e05 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -212,9 +212,9 @@ check_approxidate 'noon today' '2009-08-30 12:00:00'\n check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'\n check_approxidate 'noon today' '2009-09-01 12:00:00' '+36 hours'\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n+check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'\n check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'\n-check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'\n check_approxidate 'tea last saturday' '2009-08-29 17:00:00'\n check_approxidate 'tea last saturday' '2009-08-29 17:00:00' '-12 hours'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n\nbase-commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0\nprerequisite-patch-id: 699b43ba1ff3235d54fa068f4c9b5ab5aa58eacc\nprerequisite-patch-id: 2ab5ce03bce8b2953b418fe884f45fbd4a629337\nprerequisite-patch-id: 52e3b766b9c6f148f09682b898bf5244b22ae0ba\nprerequisite-patch-id: 1cd0e7498368f7c3b1833f12d56fefe732dd0413\n-- \n2.30.2\n\n"},{"id":"543820","messageId":"20260521105408.8222-2-taahol@utu.fi","threadId":"63156","inReplyTo":"20260521105408.8222-1-taahol@utu.fi","subject":"[PATCH v5 1/4] approxidate: make \"today\" wrap to midnight","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-21T10:54:05Z","receivedAt":"2026-05-21T10:54:35Z","isPatch":true,"body":"Although some commands do reject invalid approxidate expressions,\nin other cases those are simply evaluated as the current time.\nOftentimes that is a perfectly good compromise to handle silly\nrequests, but it isn't without rough edges.\n\nBecause of the silent acceptance, it is easy to forget that\n\"today\" isn't actually a valid approxidate format.  That is\na bit awkward because while the fallback logic of using the\ncurrent time does make some sense, there is no deliberative\ndecision behind such behavior of \"today\".  Indeed, whatever\n(non-)action \"today\" currently has, is just an accidental\nside effect.\n\nThat means \"git log --since=today\" is currently unlikely to\nprint anything at all as it tries to list commits dated with\n*future* timestamps.  Arguably it would be more useful to\nlist the commits of the current day---i.e. those made since\nmidnight.\n\nOn the other hand, \"git log --until=today\" doesn't really\nfilter commits at all.  Changing the definition of \"today\"\nwould make it return the commits made before the current day.\nThat isn't without problems though---running \"git log\n--until=today\" in the late afternoon could reasonably include\nthe work done earlier that day (as the command currently\ndoes do).\n\nStill the utility of no-op \"--until=today\" is debatable and\nperhaps outweighed by the pros of having \"--since=today\" to\nmean \"--since=midnight\".  The thing is that the approxidate\nmachinery doesn't know about its consumers, so the meaning\nof \"today\" has to be the same for \"--since\" and \"--until\".\n\nIn fact, \"git log --until=\" is documented as\n\n\t`--until=<date>`::\n\t`--before=<date>`::\n\t\tShow commits older than _<date>_,\n\nso excluding commits made today would actually match the\ndocumentation more closely.\n\nMoreover, a revision parameter \"@{today}\" is currently outright\nrejected.  Making \"today\" a valid approxidate time format could\nmake a natural way to specify the state of the ref at the start\nof the current day.\n\nBind \"today\" to new function `date_today()` as an approxidate\nspecial.  Make it return the last midnight if no specific time\nis given; i.e. retain the old behavior of \"noon today\" and such.\n\nDocument the new behavior of \"git log --since=today\" in\nrev-list-options.adoc.\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n\nNotes:\n    v4->v5: Add the usual `*num = 0` dance and a test to clarify why it's useful.\n\n Documentation/rev-list-options.adoc |  3 ++-\n date.c                              | 11 +++++++++++\n t/t0006-date.sh                     |  3 +++\n 3 files changed, 16 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc\nindex 2d195a1474..a5abadf689 100644\n--- a/Documentation/rev-list-options.adoc\n+++ b/Documentation/rev-list-options.adoc\n@@ -23,7 +23,8 @@ ordering and formatting options, such as `--reverse`.\n \n `--since=<date>`::\n `--after=<date>`::\n-\tShow commits more recent than _<date>_.\n+\tShow commits more recent than _<date>_.  As a special case,\n+\t'today' means the last midnight.\n \n `--since-as-filter=<date>`::\n \tShow all commits more recent than _<date>_. This visits\ndiff --git a/date.c b/date.c\nindex 17a95077cf..633d1176fe 100644\n--- a/date.c\n+++ b/date.c\n@@ -1192,6 +1192,16 @@ static void date_never(struct tm *tm, struct tm *now UNUSED, int *num)\n \t*num = 0;\n }\n \n+static void date_today(struct tm *tm, struct tm *now, int *num)\n+{\n+\tif (tm->tm_hour == now->tm_hour &&\n+\t    tm->tm_min == now->tm_min &&\n+\t    tm->tm_sec == now->tm_sec)\n+\t\tdate_time(tm, now, 0);\n+\t*num = 0;\n+\tupdate_tm(tm, now, 0);\n+}\n+\n static const struct special {\n \tconst char *name;\n \tvoid (*fn)(struct tm *, struct tm *, int *);\n@@ -1204,6 +1214,7 @@ static const struct special {\n \t{ \"AM\", date_am },\n \t{ \"never\", date_never },\n \t{ \"now\", date_now },\n+\t{ \"today\", date_today },\n \t{ NULL }\n };\n \ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 53ced36df4..d95afdda33 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -164,6 +164,7 @@ check_approxidate() {\n }\n \n check_approxidate now '2009-08-30 19:20:00'\n+check_approxidate today '2009-08-30 00:00:00'\n check_approxidate '5 seconds ago' '2009-08-30 19:19:55'\n check_approxidate 5.seconds.ago '2009-08-30 19:19:55'\n check_approxidate 10.minutes.ago '2009-08-30 19:10:00'\n@@ -181,12 +182,14 @@ check_approxidate '15:00' '2009-08-30 15:00:00'\n check_approxidate 'noon today' '2009-08-30 12:00:00'\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n+check_approxidate 'January 5th today pm' '2009-01-30 12:00:00'\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n \n check_approxidate 'last tuesday' '2009-08-25 19:20:00'\n check_approxidate 'July 5th' '2009-07-05 19:20:00'\n check_approxidate '06/05/2009' '2009-06-05 19:20:00'\n check_approxidate '06.05.2009' '2009-05-06 19:20:00'\n+check_approxidate 'Jan 5 today' '2009-01-30 00:00:00'\n \n check_approxidate 'Jun 6, 5AM' '2009-06-06 05:00:00'\n check_approxidate '5AM Jun 6' '2009-06-06 05:00:00'\n-- \n2.30.2\n\n"},{"id":"543816","messageId":"20260521105408.8222-4-taahol@utu.fi","threadId":"63156","inReplyTo":"20260521105408.8222-1-taahol@utu.fi","subject":"[PATCH v5 3/4] approxidate: make \"specials\" respect fixed day-of-month","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-21T10:54:07Z","receivedAt":"2026-05-21T10:54:36Z","isPatch":true,"body":"The special approxidate time formats, \"noon\" and \"tea\" differ from\n\"12pm\" and \"5pm\" by having the feature of wrapping to the previous day\nif the current time is before those hours:\n\n\tnow  -> 2026-05-13 11:00:00 +0000\n\n\t12pm -> 2026-05-13 12:00:00 +0000\n\t5pm  -> 2026-05-13 17:00:00 +0000\n\n\tnoon -> 2026-05-12 12:00:00 +0000\n\ttea  -> 2026-05-12 17:00:00 +0000\n\nHowever, that logic carries too far.  Even when the date is specified,\nthe behavior of the \"specials\" depends on the current time.  Assuming\nthe same time as above, we get:\n\n\ttoday at noon -> 2026-05-12 12:00:00 +0000 (should be 13 May)\n\t13 May at tea -> 2026-05-12 17:00:00 +0000\n\nor, using an example mentioned in date-formats.adoc:\n\n\tlast Friday at noon -> 2026-05-07 12:00:00 +0000 (should be 8 May)\n\nThe quirk seems to be rather old.  Already in 2006, Linus Torvalds\nremarked that the date yielded by \"one year ago yesterday at tea-time\"\nwas \"just silly and not even correct\".  Indeed, even today it gives:\n\n\tOne year ago yesterday at tea-time -> 2025-05-11 17:00:00 +0000\n\t  (should be 12 May)\n\nLet's fix all of those with a simple patch.  Check whether we already\nhave a specified day-of-month in `tm->tm_mday` and make `date_time()`\nstick to it.  Ensure the correct behavior with relevant tests.\n\nLinks:\n  1. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n date.c          | 6 +++++-\n t/t0006-date.sh | 4 ++++\n 2 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/date.c b/date.c\nindex 633d1176fe..1e9cfe4b6f 100644\n--- a/date.c\n+++ b/date.c\n@@ -1132,7 +1132,11 @@ static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n \n static void date_time(struct tm *tm, struct tm *now, int hour)\n {\n-\tif (tm->tm_hour < hour)\n+\t/*\n+\t * If we do not yet have a specified day, we'll use the most recent\n+\t * version of \"hour\" relative to now.  But that may be yesterday.\n+\t */\n+\tif (tm->tm_mday < 0 && tm->tm_hour < hour)\n \t\tupdate_tm(tm, now, 24*60*60);\n \ttm->tm_hour = hour;\n \ttm->tm_min = 0;\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex b9bb7a05d9..62cbada774 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -209,8 +209,12 @@ check_approxidate '6pm yesterday' '2009-08-29 18:00:00'\n check_approxidate '3:00' '2009-08-30 03:00:00'\n check_approxidate '15:00' '2009-08-30 15:00:00'\n check_approxidate 'noon today' '2009-08-30 12:00:00'\n+check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n+check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n+check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'\n check_approxidate 'January 5th today pm' '2009-01-30 12:00:00'\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'\n-- \n2.30.2\n\n"},{"id":"543819","messageId":"20260521105408.8222-5-taahol@utu.fi","threadId":"63156","inReplyTo":"20260521105408.8222-1-taahol@utu.fi","subject":"[PATCH v5 4/4] approxidate: use deferred mday adjustments for \"specials\"","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-21T10:54:08Z","receivedAt":"2026-05-21T10:54:36Z","isPatch":true,"body":"There are cases where the \"wrap-to-yesterday\" behavior of \"tea\" and\n\"noon\" should be reverted later on down the line, so that \"today tea\"\nand \"tea today\" won't yield different results.  However, the logic of\napproxidate doesn't seem to lend itself particularly well to\nsuch cases.\n\nStart tackling the issue by reusing negative values of `tm->tm_mday`\nfield for deferred date adjustments which can be easily reverted, so\nthat the default logic of the special formats only applies if we don't\nget any explicit date (mday) specification.  In particular, overwrite\nthe field with -1 in \"today\" and \"yesterday\", so that those formats will\nbe relative to the current date.  That makes specifications like \"tea\nyesterday\" behave more sensibly: instead of going backwards to the\nlast tea-time and then a day back, Git will now understand that as the\ntea-time of yesterday.\n\nReplace the call of `update_tm()` in `date_time()` with the assignment\n`tm->tm_mday = -2`.  Add the corresponding code to handle that in\n`update_tm()`, wrapping to the previous day if the field still holds\nsuch assignment, meaning that we haven't seen any better specification\nfor the day-of-month.  On the other hand, `mday=-3` would mean going\ntwo days back and so on.  Even though such functionality isn't\nactually needed by this patch, it won't add much complexity in the\ncode and is rather natural way to handle such values.\n\nAs `date_time()` won't no longer need the `now` struct, mark the\nassociated function parameters as unused.  The parameters themselves\nhave to stay, however, as those functions are called through pointers\nin `approxidate_alpha`.  Add relevant tests to cover the changes.\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n\nNotes:\n    v4->v5: apply the earlier fixup patch\n\n date.c          | 31 +++++++++++++++++++++----------\n t/t0006-date.sh |  4 ++++\n 2 files changed, 25 insertions(+), 10 deletions(-)\n\ndiff --git a/date.c b/date.c\nindex 1e9cfe4b6f..05b78d852f 100644\n--- a/date.c\n+++ b/date.c\n@@ -1071,13 +1071,22 @@ void datestamp(struct strbuf *out)\n /*\n  * Relative time update (eg \"2 days ago\").  If we haven't set the time\n  * yet, we need to set it from current time.\n+ *\n+ * The tm->tm_mday field has an additional logic of using negative values\n+ * for date adjustments: -2 means yesterday and -3 the day before that,\n+ * and so on.  The idea is to deref such adjustments until we are sure\n+ * there's no explicit mday specification in the approxidate string.\n  */\n static time_t update_tm(struct tm *tm, struct tm *now, time_t sec)\n {\n \ttime_t n;\n \n-\tif (tm->tm_mday < 0)\n+\tif (tm->tm_mday < 0) {\n+\t\tint offset = tm->tm_mday + 1;\n+\t\tif (sec == 0 && offset < 0)\n+\t\t\tsec = -offset * 24*60*60;\n \t\ttm->tm_mday = now->tm_mday;\n+\t}\n \tif (tm->tm_mon < 0)\n \t\ttm->tm_mon = now->tm_mon;\n \tif (tm->tm_year < 0) {\n@@ -1127,38 +1136,39 @@ static void date_now(struct tm *tm, struct tm *now, int *num)\n static void date_yesterday(struct tm *tm, struct tm *now, int *num)\n {\n \t*num = 0;\n+\ttm->tm_mday = -1;\n \tupdate_tm(tm, now, 24*60*60);\n }\n \n-static void date_time(struct tm *tm, struct tm *now, int hour)\n+static void date_time(struct tm *tm, int hour)\n {\n \t/*\n \t * If we do not yet have a specified day, we'll use the most recent\n \t * version of \"hour\" relative to now.  But that may be yesterday.\n \t */\n \tif (tm->tm_mday < 0 && tm->tm_hour < hour)\n-\t\tupdate_tm(tm, now, 24*60*60);\n+\t\ttm->tm_mday = -2; /* eventually handled by update_tm() */\n \ttm->tm_hour = hour;\n \ttm->tm_min = 0;\n \ttm->tm_sec = 0;\n }\n \n-static void date_midnight(struct tm *tm, struct tm *now, int *num)\n+static void date_midnight(struct tm *tm, struct tm *now UNUSED, int *num)\n {\n \tpending_number(tm, num);\n-\tdate_time(tm, now, 0);\n+\tdate_time(tm, 0);\n }\n \n-static void date_noon(struct tm *tm, struct tm *now, int *num)\n+static void date_noon(struct tm *tm, struct tm *now UNUSED, int *num)\n {\n \tpending_number(tm, num);\n-\tdate_time(tm, now, 12);\n+\tdate_time(tm, 12);\n }\n \n-static void date_tea(struct tm *tm, struct tm *now, int *num)\n+static void date_tea(struct tm *tm, struct tm *now UNUSED, int *num)\n {\n \tpending_number(tm, num);\n-\tdate_time(tm, now, 17);\n+\tdate_time(tm, 17);\n }\n \n static void date_pm(struct tm *tm, struct tm *now UNUSED, int *num)\n@@ -1201,8 +1211,9 @@ static void date_today(struct tm *tm, struct tm *now, int *num)\n \tif (tm->tm_hour == now->tm_hour &&\n \t    tm->tm_min == now->tm_min &&\n \t    tm->tm_sec == now->tm_sec)\n-\t\tdate_time(tm, now, 0);\n+\t\tdate_time(tm, 0);\n \t*num = 0;\n+\ttm->tm_mday = -1;\n \tupdate_tm(tm, now, 0);\n }\n \ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 62cbada774..9a76b84ed9 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -210,9 +210,13 @@ check_approxidate '3:00' '2009-08-30 03:00:00'\n check_approxidate '15:00' '2009-08-30 15:00:00'\n check_approxidate 'noon today' '2009-08-30 12:00:00'\n check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'\n+check_approxidate 'noon today' '2009-09-01 12:00:00' '+36 hours'\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n+check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'\n check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'\n+check_approxidate 'tea last saturday' '2009-08-29 17:00:00'\n+check_approxidate 'tea last saturday' '2009-08-29 17:00:00' '-12 hours'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'\n check_approxidate 'January 5th today pm' '2009-01-30 12:00:00'\n-- \n2.30.2\n\n"},{"id":"543817","messageId":"20260521105408.8222-3-taahol@utu.fi","threadId":"63156","inReplyTo":"20260521105408.8222-1-taahol@utu.fi","subject":"[PATCH v5 2/4] t0006: add support for approxidate test date adjustment","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-21T10:54:06Z","receivedAt":"2026-05-21T10:54:38Z","isPatch":true,"body":"t0006 uses a hard-coded test date and provides no convenient\nway to override it temporarily.  Add an optional parameter to\ncheck_approxidate to adjust the time as needed, and demonstrate\nthe feature with a new test.\n\nSigned-off-by: Tuomas Ahola <taahol@utu.fi>\n---\n t/t0006-date.sh | 33 ++++++++++++++++++++++++++++++++-\n 1 file changed, 32 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex d95afdda33..b9bb7a05d9 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -155,12 +155,41 @@ check_parse '2100-00-00 00:00:00 -11' bad\n check_parse '2100-00-00 00:00:00 +11' bad\n REQUIRE_64BIT_TIME=\n \n+add_time_offset() {\n+\tcase \"$3\" in\n+\thours)\n+\t\tunit=$(( 60*60 ))\n+\t\t;;\n+\tdays)\n+\t\tunit=$(( 24*60*60 ))\n+\t\t;;\n+\tesac\n+\toffset=$(( $2 * unit ))\n+\techo $(( $1 + offset ))\n+}\n+\n check_approxidate() {\n+\told_date=$GIT_TEST_DATE_NOW\n+\tif test \"$3\" = \"failure\"\n+\tthen\n+\t\texpection=\"$3\"\n+\telse\n+\t\texpection=${4:-success}\n+\t\toffset=\"$3\"\n+\tfi\n+\tif test -n \"$offset\"\n+\tthen\n+\t\tGIT_TEST_DATE_NOW=$(add_time_offset $old_date $offset)\n+\t\tcaption=\"$1; offset $offset\"\n+\telse\n+\t\tcaption=$1\n+\tfi\n \techo \"$1 -> $2 +0000\" >expect\n-\ttest_expect_${3:-success} \"parse approxidate ($1)\" \"\n+\ttest_expect_$expection \"parse approxidate ($caption)\" \"\n \ttest-tool date approxidate '$1' >actual &&\n \ttest_cmp expect actual\n \t\"\n+\tGIT_TEST_DATE_NOW=$old_date\n }\n \n check_approxidate now '2009-08-30 19:20:00'\n@@ -184,6 +213,8 @@ check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n check_approxidate 'January 5th today pm' '2009-01-30 12:00:00'\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n+check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'\n+check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'\n \n check_approxidate 'last tuesday' '2009-08-25 19:20:00'\n check_approxidate 'July 5th' '2009-07-05 19:20:00'\n-- \n2.30.2\n\n"},{"id":"543818","messageId":"20260521105408.8222-1-taahol@utu.fi","threadId":"63156","inReplyTo":"20260516151540.9611-1-taahol@utu.fi","subject":"[PATCH v5 0/4] approxidate: tweak special date formats","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-21T10:54:04Z","receivedAt":"2026-05-21T10:54:38Z","isPatch":true,"body":"> \"Friday noon\" asked in the morning on Sunday was parsed to be one\n> day before the specified time, which has been corrected.\n\nShould we add something about the new semantics of \"today\"?\n\nTuomas Ahola (4):\n  approxidate: make \"today\" wrap to midnight\n  t0006: add support for approxidate test date adjustment\n  approxidate: make \"specials\" respect fixed day-of-month\n  approxidate: use deferred mday adjustments for \"specials\"\n\n Documentation/rev-list-options.adoc |  3 +-\n date.c                              | 46 ++++++++++++++++++++++-------\n t/t0006-date.sh                     | 44 ++++++++++++++++++++++++++-\n 3 files changed, 81 insertions(+), 12 deletions(-)\n\nInterdiff mot v4:\ndiff --git a/date.c b/date.c\nindex 6e7cf907da..05b78d852f 100644\n--- a/date.c\n+++ b/date.c\n@@ -1206,12 +1206,13 @@ static void date_never(struct tm *tm, struct tm *now UNUSED, int *num)\n \t*num = 0;\n }\n \n-static void date_today(struct tm *tm, struct tm *now, int *num UNUSED)\n+static void date_today(struct tm *tm, struct tm *now, int *num)\n {\n \tif (tm->tm_hour == now->tm_hour &&\n \t    tm->tm_min == now->tm_min &&\n \t    tm->tm_sec == now->tm_sec)\n \t\tdate_time(tm, 0);\n+\t*num = 0;\n \ttm->tm_mday = -1;\n \tupdate_tm(tm, now, 0);\n }\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex b187b1bfc4..9a76b84ed9 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -212,13 +212,14 @@ check_approxidate 'noon today' '2009-08-30 12:00:00'\n check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'\n check_approxidate 'noon today' '2009-09-01 12:00:00' '+36 hours'\n check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n+check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'\n check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'\n-check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'\n check_approxidate 'tea last saturday' '2009-08-29 17:00:00'\n check_approxidate 'tea last saturday' '2009-08-29 17:00:00' '-12 hours'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'\n+check_approxidate 'January 5th today pm' '2009-01-30 12:00:00'\n check_approxidate '10am noon' '2009-08-29 12:00:00'\n check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'\n check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'\n\nbase-commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0\n-- \n2.30.2\n\n"},{"id":"543827","messageId":"xmqqik8g28gw.fsf@gitster.g","threadId":"63156","inReplyTo":"20260521105408.8222-1-taahol@utu.fi","subject":"Re: [PATCH v5 0/4] approxidate: tweak special date formats","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-05-21T13:33:19Z","receivedAt":"2026-05-21T13:33:22Z","isPatch":true,"body":"Tuomas Ahola <taahol@utu.fi> writes:\n\n> -static void date_today(struct tm *tm, struct tm *now, int *num UNUSED)\n> +static void date_today(struct tm *tm, struct tm *now, int *num)\n>  {\n>  \tif (tm->tm_hour == now->tm_hour &&\n>  \t    tm->tm_min == now->tm_min &&\n>  \t    tm->tm_sec == now->tm_sec)\n>  \t\tdate_time(tm, 0);\n> +\t*num = 0;\n>  \ttm->tm_mday = -1;\n>  \tupdate_tm(tm, now, 0);\n>  }\n\nHmph, what is this change about?  Does the lack of this clearing\nbreak some test?\n\nIn any case, will queue.  It seems that we are getting to the point\nof diminishing returns and better off declaring victory soonish?\n\n> diff --git a/t/t0006-date.sh b/t/t0006-date.sh\n> index b187b1bfc4..9a76b84ed9 100755\n> --- a/t/t0006-date.sh\n> +++ b/t/t0006-date.sh\n> @@ -212,13 +212,14 @@ check_approxidate 'noon today' '2009-08-30 12:00:00'\n>  check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'\n>  check_approxidate 'noon today' '2009-09-01 12:00:00' '+36 hours'\n>  check_approxidate 'noon yesterday' '2009-08-29 12:00:00'\n> +check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'\n>  check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'\n>  check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'\n> -check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'\n>  check_approxidate 'tea last saturday' '2009-08-29 17:00:00'\n>  check_approxidate 'tea last saturday' '2009-08-29 17:00:00' '-12 hours'\n>  check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'\n>  check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00' '-12 hours'\n> +check_approxidate 'January 5th today pm' '2009-01-30 12:00:00'\n>  check_approxidate '10am noon' '2009-08-29 12:00:00'\n>  check_approxidate 'January 5th yesterday' '2009-01-29 19:20:00'\n>  check_approxidate 'January 5th yesterday' '2008-12-31 19:20:00' '+2 days'\n>\n> base-commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0\n"},{"id":"543832","messageId":"20260521141409.5vXT3%taahol@utu.fi","threadId":"63156","inReplyTo":"xmqqik8g28gw.fsf@gitster.g","subject":"Re: [PATCH v5 0/4] approxidate: tweak special date formats","fromName":"Tuomas Ahola","fromEmail":"taahol@utu.fi","sentAt":"2026-05-21T14:14:09Z","receivedAt":"2026-05-21T14:14:18Z","isPatch":true,"body":"Junio C Hamano <gitster@pobox.com> wrote:\n\n> Tuomas Ahola <taahol@utu.fi> writes:\n> \n> > -static void date_today(struct tm *tm, struct tm *now, int *num UNUSED)\n> > +static void date_today(struct tm *tm, struct tm *now, int *num)\n> >  {\n> >  \tif (tm->tm_hour == now->tm_hour &&\n> >  \t    tm->tm_min == now->tm_min &&\n> >  \t    tm->tm_sec == now->tm_sec)\n> >  \t\tdate_time(tm, 0);\n> > +\t*num = 0;\n> >  \ttm->tm_mday = -1;\n> >  \tupdate_tm(tm, now, 0);\n> >  }\n> \n> Hmph, what is this change about?  Does the lack of this clearing\n> break some test?\n> \n\nAs you can see, many other date_*() functions have that same assignment, too.\nIt looked a bit off, so I first left it out, but this revision does add a\ncorner case test[*] that would fail without it.\n\n> In any case, will queue.  It seems that we are getting to the point\n> of diminishing returns and better off declaring victory soonish?\n> \n\nYes, I agree.  The patch series is effectively finished as I don't have any\nfurther itches or ideas.  And I think the series gets its work done quite well\nactually.\n\n> > +check_approxidate 'January 5th today pm' '2009-01-30 12:00:00'\n\n[*] Namely, this one.\n"}]}