{"thread":{"id":"58963","subject":"[PATCH] date.c: allow ISO 8601 reduced precision times","startedAt":"2022-12-16T03:38:15Z","lastAt":"2023-01-13T19:51:17Z","messageCount":12,"participants":["Phil Hord","Junio C Hamano","Đoàn Trần Công Danh"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"469156","messageId":"20221216033638.2582956-1-phil.hord@gmail.com","threadId":"58963","inReplyTo":null,"subject":"[PATCH] date.c: allow ISO 8601 reduced precision times","fromName":"Phil Hord","fromEmail":"phil.hord@gmail.com","sentAt":"2022-12-16T03:36:39Z","receivedAt":"2022-12-16T03:38:15Z","isPatch":true,"sender":{"key":"phil.hord@gmail.com","avatar":"https://avatars.githubusercontent.com/u/123908?v=4"},"body":"From: Phil Hord <phil.hord@gmail.com>\n\nISO 8601 permits \"reduced precision\" time representations to omit the\nseconds value or both the minutes and the seconds values.  The\nabbreviate times could look like 17:45 or 1745 to omit the seconds,\nor simply as 17 to omit both the minutes and the seconds.\n\nparse_date_basic accepts the 17:45 format but it rejects the other two.\nFix it to accept 4-digit and 2-digit time values when they follow a\nrecognized date but no time has yet been parsed.\n\nAdd tests for these formats and some others, with and without colons.\n\nBefore this change:\n\n$ test-tool date approxidate 2022-12-13T23:00 2022-12-13T2300 2022-12-13T23\n2022-12-13T23:00 -> 2022-12-14 07:00:00 +0000\n2022-12-13T2300 -> 2022-12-14 03:00:55 +0000\n2022-12-13T23 -> 2022-12-14 03:00:55 +0000\n\nAfter this change:\n\n$ test-tool date approxidate 2022-12-13T23:00 2022-12-13T2300 2022-12-13T23\n2022-12-13T23:00 -> 2022-12-14 07:00:00 +0000\n2022-12-13T2300 -> 2022-12-14 07:00:00 +0000\n2022-12-13T23 -> 2022-12-14 07:00:00 +0000\n\nNote: ISO 8601 also allows reduced precision date strings such as\n\"2022-12\" and \"2022\". This patch does not attempt to address these.\n\nReported-by: Pat LaVarre <plavarre@purestorage.com>\nSigned-off-by: Phil Hord <phil.hord@gmail.com>\n---\n date.c          | 22 +++++++++++++++++++++-\n t/t0006-date.sh |  6 ++++++\n 2 files changed, 27 insertions(+), 1 deletion(-)\n\ndiff --git a/date.c b/date.c\nindex 53bd6a7932..b011b9d6b3 100644\n--- a/date.c\n+++ b/date.c\n@@ -638,6 +638,18 @@ static inline int nodate(struct tm *tm)\n \t\ttm->tm_sec) < 0;\n }\n \n+/*\n+ * Have we filled in any part of the time yet?\n+ * We just do a binary 'and' to see if the sign bit\n+ * is set in all the values.\n+ */\n+static inline int notime(struct tm *tm)\n+{\n+\treturn (tm->tm_hour &\n+\t\ttm->tm_min &\n+\t\ttm->tm_sec) < 0;\n+}\n+\n /*\n  * We've seen a digit. Time? Year? Date?\n  */\n@@ -689,7 +701,11 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt\n \n \t/* 8 digits, compact style of ISO-8601's date: YYYYmmDD */\n \t/* 6 digits, compact style of ISO-8601's time: HHMMSS */\n-\tif (n == 8 || n == 6) {\n+\t/* 4 digits, compact style of ISO-8601's time: HHMM */\n+\t/* 2 digits, compact style of ISO-8601's time: HH */\n+\tif (n == 8 || n == 6 ||\n+\t\t(!nodate(tm) && notime(tm) &&\n+\t\t(n == 4 || n == 2))) {\n \t\tunsigned int num1 = num / 10000;\n \t\tunsigned int num2 = (num % 10000) / 100;\n \t\tunsigned int num3 = num % 100;\n@@ -698,6 +714,10 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt\n \t\telse if (n == 6 && set_time(num1, num2, num3, tm) == 0 &&\n \t\t\t *end == '.' && isdigit(end[1]))\n \t\t\tstrtoul(end + 1, &end, 10);\n+\t\telse if (n == 4)\n+\t\t\tset_time(num2, num3, 0, tm);\n+\t\telse if (n == 2)\n+\t\t\tset_time(num3, 0, 0, tm);\n \t\treturn end - date;\n \t}\n \ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 2490162071..16fb0bf4bd 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -88,6 +88,12 @@ check_parse 2008-02-14 bad\n check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'\n check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'\n check_parse '2008.02.14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'\n+check_parse '20080214T20:30:45' '2008-02-14 20:30:45 +0000'\n+check_parse '20080214T20:30' '2008-02-14 20:30:00 +0000'\n+check_parse '20080214T20' '2008-02-14 20:00:00 +0000'\n+check_parse '20080214T203045' '2008-02-14 20:30:45 +0000'\n+check_parse '20080214T2030' '2008-02-14 20:30:00 +0000'\n+check_parse '20080214T20' '2008-02-14 20:00:00 +0000'\n check_parse '20080214T203045-04:00' '2008-02-14 20:30:45 -0400'\n check_parse '20080214T203045 -04:00' '2008-02-14 20:30:45 -0400'\n check_parse '20080214T203045.019-04:00' '2008-02-14 20:30:45 -0400'\n-- \n2.39.0.56.g57e2c6ebbe\n\n"},{"id":"469157","messageId":"xmqq359gnfhe.fsf@gitster.g","threadId":"58963","inReplyTo":"20221216033638.2582956-1-phil.hord@gmail.com","subject":"Re: [PATCH] date.c: allow ISO 8601 reduced precision times","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-12-16T04:23:57Z","receivedAt":"2022-12-16T04:24:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phil Hord <phil.hord@gmail.com> writes:\n\n> From: Phil Hord <phil.hord@gmail.com>\n>\n> ISO 8601 permits \"reduced precision\" time representations to omit the\n> seconds value or both the minutes and the seconds values.  The\n> abbreviate times could look like 17:45 or 1745 to omit the seconds,\n> or simply as 17 to omit both the minutes and the seconds.\n>\n> parse_date_basic accepts the 17:45 format but it rejects the other two.\n> Fix it to accept 4-digit and 2-digit time values when they follow a\n> recognized date but no time has yet been parsed.\n\nI worry a bit that this may conflict with other approxidate\nheuristics.\n\n> $ test-tool date approxidate 2022-12-13T23:00 2022-12-13T2300 2022-12-13T23\n> 2022-12-13T23:00 -> 2022-12-14 07:00:00 +0000\n> 2022-12-13T2300 -> 2022-12-14 07:00:00 +0000\n> 2022-12-13T23 -> 2022-12-14 07:00:00 +0000\n\nAll of these may be obvious improvements, but the thing is that\nthere is nothing in the approxidate parsing code that insists on the\npresence of \"T\" to loosen the rule only for ISO-8601 case.\n\nFor example, with only 6 digits, do we still recognise our internal\ntimestamp format (i.e. seconds since epoch) without the\ndisambiguating '@' prefix?\n"},{"id":"469185","messageId":"CABURp0pWwfWO3msZ4U=_i3zkEDOq6+CUVT9Tb7KCjeBRK34Miw@mail.gmail.com","threadId":"58963","inReplyTo":"xmqq359gnfhe.fsf@gitster.g","subject":"Re: [PATCH] date.c: allow ISO 8601 reduced precision times","fromName":"Phil Hord","fromEmail":"phil.hord@gmail.com","sentAt":"2022-12-16T18:38:53Z","receivedAt":"2022-12-16T18:39:57Z","isPatch":true,"sender":{"key":"phil.hord@gmail.com","avatar":"https://avatars.githubusercontent.com/u/123908?v=4"},"body":"On Thu, Dec 15, 2022 at 8:23 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Phil Hord <phil.hord@gmail.com> writes:\n>\n> > From: Phil Hord <phil.hord@gmail.com>\n> >\n> > ISO 8601 permits \"reduced precision\" time representations to omit the\n> > seconds value or both the minutes and the seconds values.  The\n> > abbreviate times could look like 17:45 or 1745 to omit the seconds,\n> > or simply as 17 to omit both the minutes and the seconds.\n> >\n> > parse_date_basic accepts the 17:45 format but it rejects the other two.\n> > Fix it to accept 4-digit and 2-digit time values when they follow a\n> > recognized date but no time has yet been parsed.\n>\n> I worry a bit that this may conflict with other approxidate\n> heuristics.\n\nI share your concern. I tried to make the ISO matching code very\nspecific to the case where we have already matched a date, we have not\nyet matched a time value, and we see a lone 2-digit or 4-digit number\nshow up.\n\n> > $ test-tool date approxidate 2022-12-13T23:00 2022-12-13T2300 2022-12-13T23\n> > 2022-12-13T23:00 -> 2022-12-14 07:00:00 +0000\n> > 2022-12-13T2300 -> 2022-12-14 07:00:00 +0000\n> > 2022-12-13T23 -> 2022-12-14 07:00:00 +0000\n>\n> All of these may be obvious improvements, but the thing is that\n> there is nothing in the approxidate parsing code that insists on the\n> presence of \"T\" to loosen the rule only for ISO-8601 case.\n\nI considered making the T an explicit marker, but it didn't seem\nnecessary here. But the looseness of approxidate with regard to spaces\nis worrisome. That's why I added the date/no-time constraints.\n\n> For example, with only 6 digits, do we still recognise our internal\n> timestamp format (i.e. seconds since epoch) without the\n> disambiguating '@' prefix?\n\nI don't grok your example.  This change should not affect the\ninterpretation of any 6-digit number.\n\nOh, do you mean if there was _no_ delimiter before the time field?\nLike 2022-12-132300?  My change will not recognize this format, and I\nbelieve it was explicitly rejected by ISO-8601-1:2019.\n\napproxidate seems not to recognize fewer than 9 digits as an epoch\nnumber, even with the @ prefix.  But this is not because of my change.\n\ntest-tool date approxidate 123456789 12345678\n123456789 -> 1973-11-29 21:33:09 +0000\n12345678 -> 2022-12-16 18:34:02 +0000\n\ntest-tool date approxidate @123456789 @12345678\n@123456789 -> 1973-11-29 21:33:09 +0000\n@12345678 -> 2022-12-16 18:36:35 +0000\n\ntest-tool date parse 123456789 12345678\n123456789 -> 1973-11-29 13:33:09 -0800\n12345678 -> bad\n\ntest-tool date parse @123456789 @12345678\n@123456789 -> 1973-11-29 13:33:09 -0800\n@12345678 -> bad\n"},{"id":"469948","messageId":"CABURp0pqQFiM4+L0sRADTt-jmAsHcMMWLR6xa4NbqrziZjmdOQ@mail.gmail.com","threadId":"58963","inReplyTo":"CABURp0pWwfWO3msZ4U=_i3zkEDOq6+CUVT9Tb7KCjeBRK34Miw@mail.gmail.com","subject":"Re: [PATCH] date.c: allow ISO 8601 reduced precision times","fromName":"Phil Hord","fromEmail":"phil.hord@gmail.com","sentAt":"2023-01-09T06:41:50Z","receivedAt":"2023-01-09T06:42:09Z","isPatch":true,"sender":{"key":"phil.hord@gmail.com","avatar":"https://avatars.githubusercontent.com/u/123908?v=4"},"body":"> On Thu, Dec 15, 2022 at 8:23 PM Junio C Hamano <gitster@pobox.com> wrote:\n> > All of these may be obvious improvements, but the thing is that\n> > there is nothing in the approxidate parsing code that insists on the\n> > presence of \"T\" to loosen the rule only for ISO-8601 case.\n>\n> I considered making the T an explicit marker, but it didn't seem\n> necessary here. But the looseness of approxidate with regard to spaces\n> is worrisome. That's why I added the date/no-time constraints.\n>\n> > For example, with only 6 digits, do we still recognise our internal\n> > timestamp format (i.e. seconds since epoch) without the\n> > disambiguating '@' prefix?\n>\n> I don't grok your example.  This change should not affect the\n> interpretation of any 6-digit number.\n>\n> Oh, do you mean if there was _no_ delimiter before the time field?\n> Like 2022-12-132300?  My change will not recognize this format, and I\n> believe it was explicitly rejected by ISO-8601-1:2019.\n>\n> approxidate seems not to recognize fewer than 9 digits as an epoch\n> number, even with the @ prefix.  But this is not because of my change.\n>\n> test-tool date approxidate 123456789 12345678\n> 123456789 -> 1973-11-29 21:33:09 +0000\n> 12345678 -> 2022-12-16 18:34:02 +0000\n>\n> test-tool date approxidate @123456789 @12345678\n> @123456789 -> 1973-11-29 21:33:09 +0000\n> @12345678 -> 2022-12-16 18:36:35 +0000\n>\n> test-tool date parse 123456789 12345678\n> 123456789 -> 1973-11-29 13:33:09 -0800\n> 12345678 -> bad\n>\n> test-tool date parse @123456789 @12345678\n> @123456789 -> 1973-11-29 13:33:09 -0800\n> @12345678 -> bad\n\n\nDo you have any suggestions about how I can better alleviate your\nconcerns?  I don't think there are real regressions here and I tried\nto explain why.\n"},{"id":"469952","messageId":"xmqqbkn8um9q.fsf@gitster.g","threadId":"58963","inReplyTo":"CABURp0pqQFiM4+L0sRADTt-jmAsHcMMWLR6xa4NbqrziZjmdOQ@mail.gmail.com","subject":"Re: [PATCH] date.c: allow ISO 8601 reduced precision times","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-01-09T08:48:01Z","receivedAt":"2023-01-09T08:56:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phil Hord <phil.hord@gmail.com> writes:\n\n> Do you have any suggestions about how I can better alleviate your\n> concerns?  I don't think there are real regressions here and I tried\n> to explain why.\n\nOther than \"including it in a released version and waiting for\npeople to scream\", I do not think there is.  The \"next\" branch was\nmeant to be a test ground for these new features by letting\nvolunteer users to use it in their everyday development, and the\nhope was that we can catch regressions by cooking risky topics\nlonger than usual in there, but we haven't been very successful, I\nhave to say.\n\nThanks.  Let's queue it and see what happens.\n\n"},{"id":"469958","messageId":"xmqqy1qct6e8.fsf@gitster.g","threadId":"58963","inReplyTo":"xmqqbkn8um9q.fsf@gitster.g","subject":"Re: [PATCH] date.c: allow ISO 8601 reduced precision times","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-01-09T09:16:15Z","receivedAt":"2023-01-09T09:20:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Phil Hord <phil.hord@gmail.com> writes:\n>\n>> Do you have any suggestions about how I can better alleviate your\n>> concerns?  I don't think there are real regressions here and I tried\n>> to explain why.\n>\n> Other than \"including it in a released version and waiting for\n> people to scream\", I do not think there is.  The \"next\" branch was\n> meant to be a test ground for these new features by letting\n> volunteer users to use it in their everyday development, and the\n> hope was that we can catch regressions by cooking risky topics\n> longer than usual in there, but we haven't been very successful, I\n> have to say.\n>\n> Thanks.  Let's queue it and see what happens.\n\nActually, let's not queue it as-is, because it seems to break many\ntests for me.  I won't have time to take further look myself before\nlater in the week when I come back online again, though.\n\nTest Summary Report\n-------------------\nt4255-am-submodule.sh                            (Wstat: 256 (exited 1) Tests: 33 Failed: 22)\n  Failed tests:  1-6, 11-13, 15-20, 25-27, 30-33\n  Non-zero exit status: 1\nt4150-am.sh                                      (Wstat: 256 (exited 1) Tests: 87 Failed: 62)\n  Failed tests:  3, 5-7, 11-13, 15-16, 18-22, 24-25, 27-32\n                34-36, 38-46, 48, 50-52, 54, 57-61, 63-65\n                67-77, 82-85\n  Non-zero exit status: 1\nt4014-format-patch.sh                            (Wstat: 256 (exited 1) Tests: 193 Failed: 20)\n  Failed tests:  6-7, 10, 141-157\n  Non-zero exit status: 1\nt7512-status-help.sh                             (Wstat: 256 (exited 1) Tests: 44 Failed: 1)\n  Failed test:  26\n  Non-zero exit status: 1\nt3901-i18n-patch.sh                              (Wstat: 256 (exited 1) Tests: 20 Failed: 5)\n  Failed tests:  16-20\n  Non-zero exit status: 1\nt4151-am-abort.sh                                (Wstat: 256 (exited 1) Tests: 20 Failed: 7)\n  Failed tests:  2-3, 5-6, 15, 19-20\n  Non-zero exit status: 1\nt4153-am-resume-override-opts.sh                 (Wstat: 256 (exited 1) Tests: 5 Failed: 2)\n  Failed tests:  3-4\n  Non-zero exit status: 1\nt5607-clone-bundle.sh                            (Wstat: 256 (exited 1) Tests: 14 Failed: 1)\n  Failed test:  3\n  Non-zero exit status: 1\nt4152-am-subjects.sh                             (Wstat: 256 (exited 1) Tests: 13 Failed: 9)\n  Failed tests:  5-13\n  Non-zero exit status: 1\nt4253-am-keep-cr-dos.sh                          (Wstat: 256 (exited 1) Tests: 7 Failed: 4)\n  Failed tests:  3-4, 6-7\n  Non-zero exit status: 1\nt4257-am-interactive.sh                          (Wstat: 256 (exited 1) Tests: 4 Failed: 3)\n  Failed tests:  2-4\n  Non-zero exit status: 1\nt4258-am-quoted-cr.sh                            (Wstat: 256 (exited 1) Tests: 4 Failed: 2)\n  Failed tests:  3-4\n  Non-zero exit status: 1\nt4254-am-corrupt.sh                              (Wstat: 256 (exited 1) Tests: 4 Failed: 1)\n  Failed test:  3\n  Non-zero exit status: 1\nt0023-crlf-am.sh                                 (Wstat: 256 (exited 1) Tests: 2 Failed: 1)\n  Failed test:  2\n  Non-zero exit status: 1\nt4256-am-format-flowed.sh                        (Wstat: 256 (exited 1) Tests: 2 Failed: 1)\n  Failed test:  2\n  Non-zero exit status: 1\nFiles=987, Tests=28346, 137 wallclock secs (12.84 usr  4.19 sys + 799.53 cusr 1017.07 csys = 1833.63 CPU)\nResult: FAIL\ngmake[1]: *** [Makefile:62: prove] Error 1\ngmake[1]: Leaving directory '/home/gitster/w/buildfarm/seen/t'\ngmake: *** [Makefile:3196: test] Error 2\nrmdir: failed to remove '/dev/shm/testpen.2086794': Directory not empty\n"},{"id":"469965","messageId":"Y7v6jThT9GQ8Oav8@danh.dev","threadId":"58963","inReplyTo":"xmqqbkn8um9q.fsf@gitster.g","subject":"[PATCH] fixup! date.c: allow ISO 8601 reduced precision times","fromName":"Đoàn Trần Công Danh","fromEmail":"congdanhqx@gmail.com","sentAt":"2023-01-09T11:29:17Z","receivedAt":"2023-01-09T11:29:57Z","isPatch":true,"sender":{"key":"congdanhqx@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42673067?v=4"},"body":"On 2023-01-09 17:48:01+0900, Junio C Hamano <gitster@pobox.com> wrote:\n> Phil Hord <phil.hord@gmail.com> writes:\n> \n> > Do you have any suggestions about how I can better alleviate your\n> > concerns?  I don't think there are real regressions here and I tried\n> > to explain why.\n> \n> Other than \"including it in a released version and waiting for\n> people to scream\", I do not think there is.  The \"next\" branch was\n> meant to be a test ground for these new features by letting\n> volunteer users to use it in their everyday development, and the\n> hope was that we can catch regressions by cooking risky topics\n> longer than usual in there, but we haven't been very successful, I\n> have to say.\n\nWhile I think we shouldn't care much about ISO-8601, we should declare\nthat we're only conformed to RFC-3339 format instead.\n\nBelow fixup could limit the change to only ISO-8601 strings\nI'm not entirely sure if this heuristics would break those people with\n00:00:00.1234 timestamp or not (the added test cases shows that this\nchange doesn't break ISO-8601 parsing, but I don't know).\n\nOn top of Hord's patch + Junio's next, all tests pass.\n----8<----\n\nSigned-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n---\n date.c          | 21 +++++++++++++--------\n t/t0006-date.sh |  3 ++-\n 2 files changed, 15 insertions(+), 9 deletions(-)\n\ndiff --git a/date.c b/date.c\nindex b011b9d6b3..19e6787aef 100644\n--- a/date.c\n+++ b/date.c\n@@ -493,6 +493,12 @@ static int match_alpha(const char *date, struct tm *tm, int *offset)\n \t\treturn 2;\n \t}\n \n+\t/* ISO-8601 allows yyyymmDD'T'HHMMSS, with less precision */\n+\tif (*date == 'T' && isdigit(date[1])) {\n+\t\ttm->tm_hour = tm->tm_min = tm->tm_sec = 0;\n+\t\treturn strlen(\"T\");\n+\t}\n+\n \t/* BAD CRAP */\n \treturn skip_alpha(date);\n }\n@@ -639,15 +645,14 @@ static inline int nodate(struct tm *tm)\n }\n \n /*\n- * Have we filled in any part of the time yet?\n- * We just do a binary 'and' to see if the sign bit\n- * is set in all the values.\n+ * Have we seen an ISO-8601-alike date, i.e. 20220101T0,\n+ * In those special case, those fields have been set to 0\n  */\n-static inline int notime(struct tm *tm)\n+static inline int maybeiso8601(struct tm *tm)\n {\n-\treturn (tm->tm_hour &\n-\t\ttm->tm_min &\n-\t\ttm->tm_sec) < 0;\n+\treturn tm->tm_hour == 0 &&\n+\t\ttm->tm_min == 0 &&\n+\t\ttm->tm_sec == 0;\n }\n \n /*\n@@ -704,7 +709,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt\n \t/* 4 digits, compact style of ISO-8601's time: HHMM */\n \t/* 2 digits, compact style of ISO-8601's time: HH */\n \tif (n == 8 || n == 6 ||\n-\t\t(!nodate(tm) && notime(tm) &&\n+\t\t(!nodate(tm) && maybeiso8601(tm) &&\n \t\t(n == 4 || n == 2))) {\n \t\tunsigned int num1 = num / 10000;\n \t\tunsigned int num2 = (num % 10000) / 100;\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 16fb0bf4bd..130207fc04 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -93,7 +93,8 @@ check_parse '20080214T20:30' '2008-02-14 20:30:00 +0000'\n check_parse '20080214T20' '2008-02-14 20:00:00 +0000'\n check_parse '20080214T203045' '2008-02-14 20:30:45 +0000'\n check_parse '20080214T2030' '2008-02-14 20:30:00 +0000'\n-check_parse '20080214T20' '2008-02-14 20:00:00 +0000'\n+check_parse '20080214T000000.20' '2008-02-14 00:00:00 +0000'\n+check_parse '20080214T00:00:00.20' '2008-02-14 00:00:00 +0000'\n check_parse '20080214T203045-04:00' '2008-02-14 20:30:45 -0400'\n check_parse '20080214T203045 -04:00' '2008-02-14 20:30:45 -0400'\n check_parse '20080214T203045.019-04:00' '2008-02-14 20:30:45 -0400'\n\n-- \nDanh\n"},{"id":"469968","messageId":"20230109122915.30973-1-congdanhqx@gmail.com","threadId":"58963","inReplyTo":"Y7v6jThT9GQ8Oav8@danh.dev","subject":"[PATCH] date.c: limit less precision ISO-8601 with its marker","fromName":"Đoàn Trần Công Danh","fromEmail":"congdanhqx@gmail.com","sentAt":"2023-01-09T12:29:15Z","receivedAt":"2023-01-09T12:30:29Z","isPatch":true,"sender":{"key":"congdanhqx@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42673067?v=4"},"body":"The newly added heuristic to parse less precision ISO-8601 conflicts\nwith other heuristics to parse datetime-strings. E.g.:\n\n\tThu, 7 Apr 2005 15:14:13 -0700\n\nLet's limit the new heuristic to only datetime string with a 'T'\nfollowed immediately by some digits, and if we failed to parse the\nupcoming string, rollback the change.\n\nSigned-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n---\n\nHere is a better thought out change, which tried to minimize the impact of\nnew heuristics.\n\nWhile I think it's a fixup, but I still needs explaination, I think I may\nreword it's as a full patch instead.\nRange-diff:\n1:  4036e5a944 ! 1:  b703425a57 fixup! date.c: allow ISO 8601 reduced precision times\n    @@ Metadata\n     Author: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n     \n      ## Commit message ##\n    -    fixup! date.c: allow ISO 8601 reduced precision times\n    +    date.c: limit less precision ISO-8601 with its marker\n    +\n    +    The newly added heuristic to parse less precision ISO-8601 conflicts\n    +    with other heuristics to parse datetime-strings. E.g.:\n    +\n    +            Thu, 7 Apr 2005 15:14:13 -0700\n    +\n    +    Let's limit the new heuristic to only datetime string with a 'T'\n    +    followed immediately by some digits, and if we failed to parse the\n    +    upcoming string, rollback the change.\n     \n         Signed-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n     \n    @@ date.c: static int match_alpha(const char *date, struct tm *tm, int *offset)\n      \t}\n      \n     +\t/* ISO-8601 allows yyyymmDD'T'HHMMSS, with less precision */\n    -+\tif (*date == 'T' && isdigit(date[1])) {\n    -+\t\ttm->tm_hour = tm->tm_min = tm->tm_sec = 0;\n    -+\t\treturn strlen(\"T\");\n    ++\tif (*date == 'T' && isdigit(date[1]) && tm->tm_hour == -1) {\n    ++\t\ttm->tm_min = tm->tm_sec = 0;\n    ++\t\treturn 1;\n     +\t}\n     +\n      \t/* BAD CRAP */\n    @@ date.c: static inline int nodate(struct tm *tm)\n     - * We just do a binary 'and' to see if the sign bit\n     - * is set in all the values.\n     + * Have we seen an ISO-8601-alike date, i.e. 20220101T0,\n    -+ * In those special case, those fields have been set to 0\n    ++ * In which, hour is still unset,\n    ++ * and minutes and second has been set to 0.\n       */\n     -static inline int notime(struct tm *tm)\n     +static inline int maybeiso8601(struct tm *tm)\n    @@ date.c: static inline int nodate(struct tm *tm)\n     -\treturn (tm->tm_hour &\n     -\t\ttm->tm_min &\n     -\t\ttm->tm_sec) < 0;\n    -+\treturn tm->tm_hour == 0 &&\n    ++\treturn tm->tm_hour == -1 &&\n     +\t\ttm->tm_min == 0 &&\n     +\t\ttm->tm_sec == 0;\n      }\n      \n      /*\n     @@ date.c: static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt\n    - \t/* 4 digits, compact style of ISO-8601's time: HHMM */\n    - \t/* 2 digits, compact style of ISO-8601's time: HH */\n    - \tif (n == 8 || n == 6 ||\n    + \n    + \t/* 8 digits, compact style of ISO-8601's date: YYYYmmDD */\n    + \t/* 6 digits, compact style of ISO-8601's time: HHMMSS */\n    +-\t/* 4 digits, compact style of ISO-8601's time: HHMM */\n    +-\t/* 2 digits, compact style of ISO-8601's time: HH */\n    +-\tif (n == 8 || n == 6 ||\n     -\t\t(!nodate(tm) && notime(tm) &&\n    -+\t\t(!nodate(tm) && maybeiso8601(tm) &&\n    - \t\t(n == 4 || n == 2))) {\n    +-\t\t(n == 4 || n == 2))) {\n    ++\tif (n == 8 || n == 6) {\n      \t\tunsigned int num1 = num / 10000;\n      \t\tunsigned int num2 = (num % 10000) / 100;\n    + \t\tunsigned int num3 = num % 100;\n    +@@ date.c: static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt\n    + \t\telse if (n == 6 && set_time(num1, num2, num3, tm) == 0 &&\n    + \t\t\t *end == '.' && isdigit(end[1]))\n    + \t\t\tstrtoul(end + 1, &end, 10);\n    +-\t\telse if (n == 4)\n    +-\t\t\tset_time(num2, num3, 0, tm);\n    +-\t\telse if (n == 2)\n    +-\t\t\tset_time(num3, 0, 0, tm);\n    + \t\treturn end - date;\n    + \t}\n    + \n    ++\t/* reduced precision of ISO-8601's time: HHMM or HH */\n    ++\tif (maybeiso8601(tm)) {\n    ++\t\tunsigned int num1 = num;\n    ++\t\tunsigned int num2 = 0;\n    ++\t\tif (n == 4) {\n    ++\t\t\tnum1 = num / 100;\n    ++\t\t\tnum2 = num % 100;\n    ++\t\t}\n    ++\t\tif ((n == 4 || n == 2) && !nodate(tm) &&\n    ++\t\t    set_time(num1, num2, 0, tm) == 0)\n    ++\t\t\treturn n;\n    ++\t\t/*\n    ++\t\t * We thought this is an ISO-8601 time string,\n    ++\t\t * we set minutes and seconds to 0,\n    ++\t\t * turn out it isn't, rollback the change.\n    ++\t\t */\n    ++\t\ttm->tm_min = tm->tm_sec = -1;\n    ++\t}\n    ++\n    + \t/* Four-digit year or a timezone? */\n    + \tif (n == 4) {\n    + \t\tif (num <= 1400 && *offset == -1) {\n     \n      ## t/t0006-date.sh ##\n     @@ t/t0006-date.sh: check_parse '20080214T20:30' '2008-02-14 20:30:00 +0000'\n\n date.c          | 49 +++++++++++++++++++++++++++++++++----------------\n t/t0006-date.sh |  3 ++-\n 2 files changed, 35 insertions(+), 17 deletions(-)\n\ndiff --git a/date.c b/date.c\nindex b011b9d6b3..6f45eeb356 100644\n--- a/date.c\n+++ b/date.c\n@@ -493,6 +493,12 @@ static int match_alpha(const char *date, struct tm *tm, int *offset)\n \t\treturn 2;\n \t}\n \n+\t/* ISO-8601 allows yyyymmDD'T'HHMMSS, with less precision */\n+\tif (*date == 'T' && isdigit(date[1]) && tm->tm_hour == -1) {\n+\t\ttm->tm_min = tm->tm_sec = 0;\n+\t\treturn 1;\n+\t}\n+\n \t/* BAD CRAP */\n \treturn skip_alpha(date);\n }\n@@ -639,15 +645,15 @@ static inline int nodate(struct tm *tm)\n }\n \n /*\n- * Have we filled in any part of the time yet?\n- * We just do a binary 'and' to see if the sign bit\n- * is set in all the values.\n+ * Have we seen an ISO-8601-alike date, i.e. 20220101T0,\n+ * In which, hour is still unset,\n+ * and minutes and second has been set to 0.\n  */\n-static inline int notime(struct tm *tm)\n+static inline int maybeiso8601(struct tm *tm)\n {\n-\treturn (tm->tm_hour &\n-\t\ttm->tm_min &\n-\t\ttm->tm_sec) < 0;\n+\treturn tm->tm_hour == -1 &&\n+\t\ttm->tm_min == 0 &&\n+\t\ttm->tm_sec == 0;\n }\n \n /*\n@@ -701,11 +707,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt\n \n \t/* 8 digits, compact style of ISO-8601's date: YYYYmmDD */\n \t/* 6 digits, compact style of ISO-8601's time: HHMMSS */\n-\t/* 4 digits, compact style of ISO-8601's time: HHMM */\n-\t/* 2 digits, compact style of ISO-8601's time: HH */\n-\tif (n == 8 || n == 6 ||\n-\t\t(!nodate(tm) && notime(tm) &&\n-\t\t(n == 4 || n == 2))) {\n+\tif (n == 8 || n == 6) {\n \t\tunsigned int num1 = num / 10000;\n \t\tunsigned int num2 = (num % 10000) / 100;\n \t\tunsigned int num3 = num % 100;\n@@ -714,13 +716,28 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt\n \t\telse if (n == 6 && set_time(num1, num2, num3, tm) == 0 &&\n \t\t\t *end == '.' && isdigit(end[1]))\n \t\t\tstrtoul(end + 1, &end, 10);\n-\t\telse if (n == 4)\n-\t\t\tset_time(num2, num3, 0, tm);\n-\t\telse if (n == 2)\n-\t\t\tset_time(num3, 0, 0, tm);\n \t\treturn end - date;\n \t}\n \n+\t/* reduced precision of ISO-8601's time: HHMM or HH */\n+\tif (maybeiso8601(tm)) {\n+\t\tunsigned int num1 = num;\n+\t\tunsigned int num2 = 0;\n+\t\tif (n == 4) {\n+\t\t\tnum1 = num / 100;\n+\t\t\tnum2 = num % 100;\n+\t\t}\n+\t\tif ((n == 4 || n == 2) && !nodate(tm) &&\n+\t\t    set_time(num1, num2, 0, tm) == 0)\n+\t\t\treturn n;\n+\t\t/*\n+\t\t * We thought this is an ISO-8601 time string,\n+\t\t * we set minutes and seconds to 0,\n+\t\t * turn out it isn't, rollback the change.\n+\t\t */\n+\t\ttm->tm_min = tm->tm_sec = -1;\n+\t}\n+\n \t/* Four-digit year or a timezone? */\n \tif (n == 4) {\n \t\tif (num <= 1400 && *offset == -1) {\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 16fb0bf4bd..130207fc04 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -93,7 +93,8 @@ check_parse '20080214T20:30' '2008-02-14 20:30:00 +0000'\n check_parse '20080214T20' '2008-02-14 20:00:00 +0000'\n check_parse '20080214T203045' '2008-02-14 20:30:45 +0000'\n check_parse '20080214T2030' '2008-02-14 20:30:00 +0000'\n-check_parse '20080214T20' '2008-02-14 20:00:00 +0000'\n+check_parse '20080214T000000.20' '2008-02-14 00:00:00 +0000'\n+check_parse '20080214T00:00:00.20' '2008-02-14 00:00:00 +0000'\n check_parse '20080214T203045-04:00' '2008-02-14 20:30:45 -0400'\n check_parse '20080214T203045 -04:00' '2008-02-14 20:30:45 -0400'\n check_parse '20080214T203045.019-04:00' '2008-02-14 20:30:45 -0400'\n-- \n2.39.0.287.g690a66fa66\n\n"},{"id":"469996","messageId":"CABURp0oAuT702PfFppir-u9psOt0ETDXeO+_8TwB5UO-edoaKw@mail.gmail.com","threadId":"58963","inReplyTo":"xmqqy1qct6e8.fsf@gitster.g","subject":"Re: [PATCH] date.c: allow ISO 8601 reduced precision times","fromName":"Phil Hord","fromEmail":"phil.hord@gmail.com","sentAt":"2023-01-09T18:30:10Z","receivedAt":"2023-01-09T18:34:46Z","isPatch":true,"sender":{"key":"phil.hord@gmail.com","avatar":"https://avatars.githubusercontent.com/u/123908?v=4"},"body":"On Mon, Jan 9, 2023 at 1:16 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n> > Phil Hord <phil.hord@gmail.com> writes:\n> >\n> >> Do you have any suggestions about how I can better alleviate your\n> >> concerns?  I don't think there are real regressions here and I tried\n> >> to explain why.\n> >\n> > Other than \"including it in a released version and waiting for\n> > people to scream\", I do not think there is.  The \"next\" branch was\n> > meant to be a test ground for these new features by letting\n> > volunteer users to use it in their everyday development, and the\n> > hope was that we can catch regressions by cooking risky topics\n> > longer than usual in there, but we haven't been very successful, I\n> > have to say.\n> >\n> > Thanks.  Let's queue it and see what happens.\n>\n> Actually, let's not queue it as-is, because it seems to break many\n> tests for me.  I won't have time to take further look myself before\n> later in the week when I come back online again, though.\n\nOh, wow. For me as well.  I thought I ran all the tests before\nfinishing up, but I guess I was too focused on the single test module.\nI apologize for the oversight.\n"},{"id":"469999","messageId":"CABURp0o5hhr0u+=4w0u4dPphFXS0gM4W_QtX+2hLLb0v3ErnJg@mail.gmail.com","threadId":"58963","inReplyTo":"20230109122915.30973-1-congdanhqx@gmail.com","subject":"Re: [PATCH] date.c: limit less precision ISO-8601 with its marker","fromName":"Phil Hord","fromEmail":"phil.hord@gmail.com","sentAt":"2023-01-09T18:57:58Z","receivedAt":"2023-01-09T18:59:17Z","isPatch":true,"sender":{"key":"phil.hord@gmail.com","avatar":"https://avatars.githubusercontent.com/u/123908?v=4"},"body":"On Mon, Jan 9, 2023 at 4:29 AM Đoàn Trần Công Danh <congdanhqx@gmail.com> wrote:\n>\n> The newly added heuristic to parse less precision ISO-8601 conflicts\n> with other heuristics to parse datetime-strings. E.g.:\n>\n>         Thu, 7 Apr 2005 15:14:13 -0700\n>\n> Let's limit the new heuristic to only datetime string with a 'T'\n> followed immediately by some digits, and if we failed to parse the\n> upcoming string, rollback the change.\n>\n> Signed-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n> ---\n>\n> Here is a better thought out change, which tried to minimize the impact of\n> new heuristics.\n>\n> While I think it's a fixup, but I still needs explaination, I think I may\n> reword it's as a full patch instead.\n> Range-diff:\n> 1:  4036e5a944 ! 1:  b703425a57 fixup! date.c: allow ISO 8601 reduced precision times\n>     @@ Metadata\n>      Author: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n>\n>       ## Commit message ##\n>     -    fixup! date.c: allow ISO 8601 reduced precision times\n>     +    date.c: limit less precision ISO-8601 with its marker\n>     +\n>     +    The newly added heuristic to parse less precision ISO-8601 conflicts\n>     +    with other heuristics to parse datetime-strings. E.g.:\n>     +\n>     +            Thu, 7 Apr 2005 15:14:13 -0700\n>     +\n>     +    Let's limit the new heuristic to only datetime string with a 'T'\n>     +    followed immediately by some digits, and if we failed to parse the\n>     +    upcoming string, rollback the change.\n>\n>          Signed-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n>\n>     @@ date.c: static int match_alpha(const char *date, struct tm *tm, int *offset)\n>         }\n>\n>      +  /* ISO-8601 allows yyyymmDD'T'HHMMSS, with less precision */\n>     -+  if (*date == 'T' && isdigit(date[1])) {\n>     -+          tm->tm_hour = tm->tm_min = tm->tm_sec = 0;\n>     -+          return strlen(\"T\");\n>     ++  if (*date == 'T' && isdigit(date[1]) && tm->tm_hour == -1) {\n>     ++          tm->tm_min = tm->tm_sec = 0;\n>     ++          return 1;\n>      +  }\n>      +\n>         /* BAD CRAP */\n>     @@ date.c: static inline int nodate(struct tm *tm)\n>      - * We just do a binary 'and' to see if the sign bit\n>      - * is set in all the values.\n>      + * Have we seen an ISO-8601-alike date, i.e. 20220101T0,\n>     -+ * In those special case, those fields have been set to 0\n>     ++ * In which, hour is still unset,\n>     ++ * and minutes and second has been set to 0.\n>        */\n>      -static inline int notime(struct tm *tm)\n>      +static inline int maybeiso8601(struct tm *tm)\n>     @@ date.c: static inline int nodate(struct tm *tm)\n>      -  return (tm->tm_hour &\n>      -          tm->tm_min &\n>      -          tm->tm_sec) < 0;\n>     -+  return tm->tm_hour == 0 &&\n>     ++  return tm->tm_hour == -1 &&\n>      +          tm->tm_min == 0 &&\n>      +          tm->tm_sec == 0;\n>       }\n>\n>       /*\n>      @@ date.c: static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt\n>     -   /* 4 digits, compact style of ISO-8601's time: HHMM */\n>     -   /* 2 digits, compact style of ISO-8601's time: HH */\n>     -   if (n == 8 || n == 6 ||\n>     +\n>     +   /* 8 digits, compact style of ISO-8601's date: YYYYmmDD */\n>     +   /* 6 digits, compact style of ISO-8601's time: HHMMSS */\n>     +-  /* 4 digits, compact style of ISO-8601's time: HHMM */\n>     +-  /* 2 digits, compact style of ISO-8601's time: HH */\n>     +-  if (n == 8 || n == 6 ||\n>      -          (!nodate(tm) && notime(tm) &&\n>     -+          (!nodate(tm) && maybeiso8601(tm) &&\n>     -           (n == 4 || n == 2))) {\n>     +-          (n == 4 || n == 2))) {\n>     ++  if (n == 8 || n == 6) {\n>                 unsigned int num1 = num / 10000;\n>                 unsigned int num2 = (num % 10000) / 100;\n>     +           unsigned int num3 = num % 100;\n>     +@@ date.c: static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt\n>     +           else if (n == 6 && set_time(num1, num2, num3, tm) == 0 &&\n>     +                    *end == '.' && isdigit(end[1]))\n>     +                   strtoul(end + 1, &end, 10);\n>     +-          else if (n == 4)\n>     +-                  set_time(num2, num3, 0, tm);\n>     +-          else if (n == 2)\n>     +-                  set_time(num3, 0, 0, tm);\n>     +           return end - date;\n>     +   }\n>     +\n>     ++  /* reduced precision of ISO-8601's time: HHMM or HH */\n>     ++  if (maybeiso8601(tm)) {\n>     ++          unsigned int num1 = num;\n>     ++          unsigned int num2 = 0;\n>     ++          if (n == 4) {\n>     ++                  num1 = num / 100;\n>     ++                  num2 = num % 100;\n>     ++          }\n>     ++          if ((n == 4 || n == 2) && !nodate(tm) &&\n>     ++              set_time(num1, num2, 0, tm) == 0)\n>     ++                  return n;\n>     ++          /*\n>     ++           * We thought this is an ISO-8601 time string,\n>     ++           * we set minutes and seconds to 0,\n>     ++           * turn out it isn't, rollback the change.\n>     ++           */\n>     ++          tm->tm_min = tm->tm_sec = -1;\n>     ++  }\n>     ++\n>     +   /* Four-digit year or a timezone? */\n>     +   if (n == 4) {\n>     +           if (num <= 1400 && *offset == -1) {\n>\n>       ## t/t0006-date.sh ##\n>      @@ t/t0006-date.sh: check_parse '20080214T20:30' '2008-02-14 20:30:00 +0000'\n>\n>  date.c          | 49 +++++++++++++++++++++++++++++++++----------------\n>  t/t0006-date.sh |  3 ++-\n>  2 files changed, 35 insertions(+), 17 deletions(-)\n>\n> diff --git a/date.c b/date.c\n> index b011b9d6b3..6f45eeb356 100644\n> --- a/date.c\n> +++ b/date.c\n> @@ -493,6 +493,12 @@ static int match_alpha(const char *date, struct tm *tm, int *offset)\n>                 return 2;\n>         }\n>\n> +       /* ISO-8601 allows yyyymmDD'T'HHMMSS, with less precision */\n> +       if (*date == 'T' && isdigit(date[1]) && tm->tm_hour == -1) {\n> +               tm->tm_min = tm->tm_sec = 0;\n> +               return 1;\n> +       }\n> +\n>         /* BAD CRAP */\n>         return skip_alpha(date);\n>  }\n> @@ -639,15 +645,15 @@ static inline int nodate(struct tm *tm)\n>  }\n>\n>  /*\n> - * Have we filled in any part of the time yet?\n> - * We just do a binary 'and' to see if the sign bit\n> - * is set in all the values.\n> + * Have we seen an ISO-8601-alike date, i.e. 20220101T0,\n> + * In which, hour is still unset,\n> + * and minutes and second has been set to 0.\n>   */\n> -static inline int notime(struct tm *tm)\n> +static inline int maybeiso8601(struct tm *tm)\n>  {\n> -       return (tm->tm_hour &\n> -               tm->tm_min &\n> -               tm->tm_sec) < 0;\n> +       return tm->tm_hour == -1 &&\n> +               tm->tm_min == 0 &&\n> +               tm->tm_sec == 0;\n>  }\n>\n>  /*\n> @@ -701,11 +707,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt\n>\n>         /* 8 digits, compact style of ISO-8601's date: YYYYmmDD */\n>         /* 6 digits, compact style of ISO-8601's time: HHMMSS */\n> -       /* 4 digits, compact style of ISO-8601's time: HHMM */\n> -       /* 2 digits, compact style of ISO-8601's time: HH */\n> -       if (n == 8 || n == 6 ||\n> -               (!nodate(tm) && notime(tm) &&\n> -               (n == 4 || n == 2))) {\n> +       if (n == 8 || n == 6) {\n>                 unsigned int num1 = num / 10000;\n>                 unsigned int num2 = (num % 10000) / 100;\n>                 unsigned int num3 = num % 100;\n> @@ -714,13 +716,28 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt\n>                 else if (n == 6 && set_time(num1, num2, num3, tm) == 0 &&\n>                          *end == '.' && isdigit(end[1]))\n>                         strtoul(end + 1, &end, 10);\n> -               else if (n == 4)\n> -                       set_time(num2, num3, 0, tm);\n> -               else if (n == 2)\n> -                       set_time(num3, 0, 0, tm);\n>                 return end - date;\n>         }\n>\n> +       /* reduced precision of ISO-8601's time: HHMM or HH */\n> +       if (maybeiso8601(tm)) {\n> +               unsigned int num1 = num;\n> +               unsigned int num2 = 0;\n> +               if (n == 4) {\n> +                       num1 = num / 100;\n> +                       num2 = num % 100;\n> +               }\n> +               if ((n == 4 || n == 2) && !nodate(tm) &&\n> +                   set_time(num1, num2, 0, tm) == 0)\n> +                       return n;\n> +               /*\n> +                * We thought this is an ISO-8601 time string,\n> +                * we set minutes and seconds to 0,\n> +                * turn out it isn't, rollback the change.\n> +                */\n> +               tm->tm_min = tm->tm_sec = -1;\n> +       }\n> +\n>         /* Four-digit year or a timezone? */\n>         if (n == 4) {\n>                 if (num <= 1400 && *offset == -1) {\n> diff --git a/t/t0006-date.sh b/t/t0006-date.sh\n> index 16fb0bf4bd..130207fc04 100755\n> --- a/t/t0006-date.sh\n> +++ b/t/t0006-date.sh\n> @@ -93,7 +93,8 @@ check_parse '20080214T20:30' '2008-02-14 20:30:00 +0000'\n>  check_parse '20080214T20' '2008-02-14 20:00:00 +0000'\n>  check_parse '20080214T203045' '2008-02-14 20:30:45 +0000'\n>  check_parse '20080214T2030' '2008-02-14 20:30:00 +0000'\n> -check_parse '20080214T20' '2008-02-14 20:00:00 +0000'\n> +check_parse '20080214T000000.20' '2008-02-14 00:00:00 +0000'\n> +check_parse '20080214T00:00:00.20' '2008-02-14 00:00:00 +0000'\n>  check_parse '20080214T203045-04:00' '2008-02-14 20:30:45 -0400'\n>  check_parse '20080214T203045 -04:00' '2008-02-14 20:30:45 -0400'\n>  check_parse '20080214T203045.019-04:00' '2008-02-14 20:30:45 -0400'\n> --\n> 2.39.0.287.g690a66fa66\n>\n\nThanks, Đoàn.  LGTM, and much safer.\n"},{"id":"470084","messageId":"20230111001003.10916-1-congdanhqx@gmail.com","threadId":"58963","inReplyTo":"20221216033638.2582956-1-phil.hord@gmail.com","subject":"[PATCH v2] date.c: allow ISO 8601 reduced precision times","fromName":"Đoàn Trần Công Danh","fromEmail":"congdanhqx@gmail.com","sentAt":"2023-01-11T00:10:03Z","receivedAt":"2023-01-11T00:10:18Z","isPatch":true,"sender":{"key":"congdanhqx@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42673067?v=4"},"body":"ISO 8601 permits \"reduced precision\" time representations to omit the\nseconds value or both the minutes and the seconds values.  The\nabbreviate times could look like 17:45 or 1745 to omit the seconds,\nor simply as 17 to omit both the minutes and the seconds.\n\nparse_date_basic accepts the 17:45 format but it rejects the other two.\nChange it to accept 4-digit and 2-digit time values when they follow a\nrecognized date and a 'T'.\n\nBefore this change:\n\n$ TZ=UTC test-tool date approxidate 2022-12-13T23:00 2022-12-13T2300 2022-12-13T23\n2022-12-13T23:00 -> 2022-12-13 23:00:00 +0000\n2022-12-13T2300 -> 2022-12-13 23:54:13 +0000\n2022-12-13T23 -> 2022-12-13 23:54:13 +0000\n\nAfter this change:\n\n$ TZ=UTC helper/test-tool date approxidate 2022-12-13T23:00 2022-12-13T2300 2022-12-13T23\n2022-12-13T23:00 -> 2022-12-13 23:00:00 +0000\n2022-12-13T2300 -> 2022-12-13 23:00:00 +0000\n2022-12-13T23 -> 2022-12-13 23:00:00 +0000\n\nNote: ISO 8601 also allows reduced precision date strings such as\n\"2022-12\" and \"2022\". This patch does not attempt to address these.\n\nReported-by: Pat LaVarre <plavarre@purestorage.com>\nSigned-off-by: Phil Hord <phil.hord@gmail.com>\nSigned-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n---\n\nSince this is a complete re-implementation from Phil Hord's version.\nI'm reassigning the author to me.\n\nThis version change the implementation to only treat the string as ISO8601 if\na 'T' existed and date has been parsed. I also added a test for parsing\nRFC-822, which Hord accidentally broke.\n\nThe commit message has been changed:\n* The example has been changed to be independent from local timezone\n* Remove the mention of adding test-cases, since it's obviously necessary.\n\n date.c          | 37 +++++++++++++++++++++++++++++++++++++\n t/t0006-date.sh |  8 ++++++++\n 2 files changed, 45 insertions(+)\n\ndiff --git a/date.c b/date.c\nindex 53bd6a7932..6f45eeb356 100644\n--- a/date.c\n+++ b/date.c\n@@ -493,6 +493,12 @@ static int match_alpha(const char *date, struct tm *tm, int *offset)\n \t\treturn 2;\n \t}\n \n+\t/* ISO-8601 allows yyyymmDD'T'HHMMSS, with less precision */\n+\tif (*date == 'T' && isdigit(date[1]) && tm->tm_hour == -1) {\n+\t\ttm->tm_min = tm->tm_sec = 0;\n+\t\treturn 1;\n+\t}\n+\n \t/* BAD CRAP */\n \treturn skip_alpha(date);\n }\n@@ -638,6 +644,18 @@ static inline int nodate(struct tm *tm)\n \t\ttm->tm_sec) < 0;\n }\n \n+/*\n+ * Have we seen an ISO-8601-alike date, i.e. 20220101T0,\n+ * In which, hour is still unset,\n+ * and minutes and second has been set to 0.\n+ */\n+static inline int maybeiso8601(struct tm *tm)\n+{\n+\treturn tm->tm_hour == -1 &&\n+\t\ttm->tm_min == 0 &&\n+\t\ttm->tm_sec == 0;\n+}\n+\n /*\n  * We've seen a digit. Time? Year? Date?\n  */\n@@ -701,6 +719,25 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt\n \t\treturn end - date;\n \t}\n \n+\t/* reduced precision of ISO-8601's time: HHMM or HH */\n+\tif (maybeiso8601(tm)) {\n+\t\tunsigned int num1 = num;\n+\t\tunsigned int num2 = 0;\n+\t\tif (n == 4) {\n+\t\t\tnum1 = num / 100;\n+\t\t\tnum2 = num % 100;\n+\t\t}\n+\t\tif ((n == 4 || n == 2) && !nodate(tm) &&\n+\t\t    set_time(num1, num2, 0, tm) == 0)\n+\t\t\treturn n;\n+\t\t/*\n+\t\t * We thought this is an ISO-8601 time string,\n+\t\t * we set minutes and seconds to 0,\n+\t\t * turn out it isn't, rollback the change.\n+\t\t */\n+\t\ttm->tm_min = tm->tm_sec = -1;\n+\t}\n+\n \t/* Four-digit year or a timezone? */\n \tif (n == 4) {\n \t\tif (num <= 1400 && *offset == -1) {\ndiff --git a/t/t0006-date.sh b/t/t0006-date.sh\nindex 2490162071..e18b160286 100755\n--- a/t/t0006-date.sh\n+++ b/t/t0006-date.sh\n@@ -88,6 +88,13 @@ check_parse 2008-02-14 bad\n check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'\n check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'\n check_parse '2008.02.14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'\n+check_parse '20080214T20:30:45' '2008-02-14 20:30:45 +0000'\n+check_parse '20080214T20:30' '2008-02-14 20:30:00 +0000'\n+check_parse '20080214T20' '2008-02-14 20:00:00 +0000'\n+check_parse '20080214T203045' '2008-02-14 20:30:45 +0000'\n+check_parse '20080214T2030' '2008-02-14 20:30:00 +0000'\n+check_parse '20080214T000000.20' '2008-02-14 00:00:00 +0000'\n+check_parse '20080214T00:00:00.20' '2008-02-14 00:00:00 +0000'\n check_parse '20080214T203045-04:00' '2008-02-14 20:30:45 -0400'\n check_parse '20080214T203045 -04:00' '2008-02-14 20:30:45 -0400'\n check_parse '20080214T203045.019-04:00' '2008-02-14 20:30:45 -0400'\n@@ -99,6 +106,7 @@ check_parse '2008-02-14 20:30:45 -05' '2008-02-14 20:30:45 -0500'\n check_parse '2008-02-14 20:30:45 -:30' '2008-02-14 20:30:45 +0000'\n check_parse '2008-02-14 20:30:45 -05:00' '2008-02-14 20:30:45 -0500'\n check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 -0500' EST5\n+check_parse 'Thu, 7 Apr 2005 15:14:13 -0700' '2005-04-07 15:14:13 -0700'\n \n check_approxidate() {\n \techo \"$1 -> $2 +0000\" >expect\n-- \n2.39.0.287.g690a66fa66\n\n"},{"id":"470300","messageId":"xmqqlem62oyr.fsf@gitster.g","threadId":"58963","inReplyTo":"20230111001003.10916-1-congdanhqx@gmail.com","subject":"Re: [PATCH v2] date.c: allow ISO 8601 reduced precision times","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-01-13T19:50:52Z","receivedAt":"2023-01-13T19:51:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Đoàn Trần Công Danh  <congdanhqx@gmail.com> writes:\n\n> ISO 8601 permits \"reduced precision\" time representations to omit the\n> seconds value or both the minutes and the seconds values.  The\n> abbreviate times could look like 17:45 or 1745 to omit the seconds,\n> or simply as 17 to omit both the minutes and the seconds.\n>\n> parse_date_basic accepts the 17:45 format but it rejects the other two.\n> Change it to accept 4-digit and 2-digit time values when they follow a\n> recognized date and a 'T'.\n>\n> Before this change:\n>\n> $ TZ=UTC test-tool date approxidate 2022-12-13T23:00 2022-12-13T2300 2022-12-13T23\n> 2022-12-13T23:00 -> 2022-12-13 23:00:00 +0000\n> 2022-12-13T2300 -> 2022-12-13 23:54:13 +0000\n> 2022-12-13T23 -> 2022-12-13 23:54:13 +0000\n>\n> After this change:\n>\n> $ TZ=UTC helper/test-tool date approxidate 2022-12-13T23:00 2022-12-13T2300 2022-12-13T23\n> 2022-12-13T23:00 -> 2022-12-13 23:00:00 +0000\n> 2022-12-13T2300 -> 2022-12-13 23:00:00 +0000\n> 2022-12-13T23 -> 2022-12-13 23:00:00 +0000\n>\n> Note: ISO 8601 also allows reduced precision date strings such as\n> \"2022-12\" and \"2022\". This patch does not attempt to address these.\n>\n> Reported-by: Pat LaVarre <plavarre@purestorage.com>\n> Signed-off-by: Phil Hord <phil.hord@gmail.com>\n> Signed-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>\n> ---\n>\n> Since this is a complete re-implementation from Phil Hord's version.\n> I'm reassigning the author to me.\n>\n> This version change the implementation to only treat the string as ISO8601 if\n> a 'T' existed and date has been parsed. I also added a test for parsing\n> RFC-822, which Hord accidentally broke.\n>\n> The commit message has been changed:\n> * The example has been changed to be independent from local timezone\n> * Remove the mention of adding test-cases, since it's obviously necessary.\n\nWill replace Phil's patch with this (hence even under your\nauthorship, the topic name will be reused).\n\nThanks, both.\n"}]}