{"thread":{"id":"2513","subject":"git's rev-parse.c function show_datestring presumes gnu date","startedAt":"2005-11-14T23:02:24Z","lastAt":"2005-11-15T03:29:06Z","messageCount":3,"participants":["Randal L. Schwartz","Linus Torvalds"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"11852","messageId":"86zmo6vnnj.fsf@blue.stonehenge.com","threadId":"2513","inReplyTo":null,"subject":"git's rev-parse.c function show_datestring presumes gnu date","fromName":"Randal L. Schwartz","fromEmail":"merlyn@stonehenge.com","sentAt":"2005-11-14T23:02:24Z","receivedAt":"2005-11-14T23:02:24Z","isPatch":false,"sender":{"key":"merlyn@stonehenge.com","avatar":"https://gravatar.com/avatar/dc528d210743ff0333e6213f9ee7b33b23f1b7bc1f3c5a8c2d819074ecd7ab19?d=mp&s=160"},"body":"\ngit-rev-parse calls GNU date externally for --since, --after, --before,\nand --until, which will fail on at least OpenBSD and Mac OSX (Darwin).\n\nAt a minimum, this code can be commented out on such platforms.  Better\nwould be to have some sort of suitable replacement.\n\nPerhaps the Makefile can test if \"date\" is GNU date as well.\n\n-- \nRandal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095\n<merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>\nPerl/Unix/security consulting, Technical writing, Comedy, etc. etc.\nSee PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!\n"},{"id":"11854","messageId":"Pine.LNX.4.64.0511141504530.3263@g5.osdl.org","threadId":"2513","inReplyTo":"86zmo6vnnj.fsf@blue.stonehenge.com","subject":"Re: git's rev-parse.c function show_datestring presumes gnu date","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-11-14T23:08:45Z","receivedAt":"2005-11-14T23:08:45Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 14 Nov 2005, Randal L. Schwartz wrote:\n> \n> git-rev-parse calls GNU date externally for --since, --after, --before,\n> and --until, which will fail on at least OpenBSD and Mac OSX (Darwin).\n> \n> At a minimum, this code can be commented out on such platforms.  Better\n> would be to have some sort of suitable replacement.\n\nI really wanted to do the date parsing myself, rather than call out to \ndate, but I got lazy.\n\nIt shouldn't be all that difficult to do some trivial cases (gnu date \nhandles totally insane things, we could easily do with just a simpler \nthing). \n\nAnybody want to handle\n - partial real dates (\"August 5th\" - figure out the year automatically, \n   and notice that it might be _last_ year since the date only makes \n   sense if it's in the past)\n - relative dates (\"5 days ago\", \"yesterday\", \"one week ago\")\n\nIt shouldn't be that hard, and if you do _not_ touch the date.c functions \n(which have to be totally dependable) you can be pretty damn lazy and lax \nand just guess a lot.\n\n\t\tLinus\n"},{"id":"11865","messageId":"Pine.LNX.4.64.0511141857121.3263@g5.osdl.org","threadId":"2513","inReplyTo":"Pine.LNX.4.64.0511141504530.3263@g5.osdl.org","subject":"Re: git's rev-parse.c function show_datestring presumes gnu date","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-11-15T03:29:06Z","receivedAt":"2005-11-15T03:29:06Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 14 Nov 2005, Linus Torvalds wrote:\n> \n> It shouldn't be all that difficult to do some trivial cases (gnu date \n> handles totally insane things, we could easily do with just a simpler \n> thing). \n\nOk. This is the insane patch to do this.\n\nIt really isn't very careful, and the reason I call it \"approxidate()\" \nwill become obvious when you look at the code. It is very liberal in what \nit accepts, to the point where sometimes the results may not make a whole \nlot of sense.\n\nIt accepts \"last week\" as a date string, by virtue of \"last\" parsing as \nthe number 1, and it totally ignoring superfluous fluff like \"ago\", so \n\"last week\" ends up being exactly the same thing as \"1 week ago\". Fine so \nfar.\n\nIt has strange side effects: \"last december\" will actually parse as \"Dec \n1\", which actually _does_ turn out right, because it will then notice that \nit's not December yet, so it will decide that you must be talking about a \ndate last year. So it actually gets it right, but it's kind of for the \n\"wrong\" reasons.\n\nIt also accepts the numbers 1..10 in string format (\"one\" .. \"ten\"), so \nyou can do \"ten weeks ago\" or \"ten hours ago\" and it will do the right \nthing.\n\nBut it will do some really strange thigns too: the string \"this will last \nforever\", will not recognize anyting but \"last\", which is recognized as \n\"1\", which since it doesn't understand anything else it will think is the \nday of the month. So if you do\n\n\tgitk --since=\"this will last forever\"\n\nthe date will actually parse as the first day of the current month.\n\nAnd it will parse the string \"now\" as \"now\", but only because it doesn't \nunderstand it at all, and it makes everything relative to \"now\".\n\nSimilarly, it doesn't actually parse the \"ago\" or \"from now\", so \"2 weeks \nago\" is exactly the same as \"2 weeks from now\". It's the current date \nminus 14 days.\n\nBut hey, it's probably better (and certainly faster) than depending on GNU \ndate. So now you can portably do things like\n\n\tgitk --since=\"two weeks and three days ago\"\n\tgit log --since=\"July 5\"\n\tgit-whatchanged --since=\"10 hours ago\"\n\tgit log --since=\"last october\"\n\nand it will actually do exactly what you thought it would do (I think). It \nwill count 17 days backwards, and it will do so even if you don't have GNU \ndate installed.\n\n(I don't do \"last monday\" or similar yet, but I can extend it to that too \nif people want).\n\nIt was kind of fun trying to write code that uses such totally relaxed \n\"understanding\" of dates yet tries to get it right for the trivial cases. \nThe result should be mixed with a few strange preprocessor tricks, and be \nsubmitted for the IOCCC ;)\n\nFeel free to try it out, and see how many strange dates it gets right. Or \nwrong.\n\nAnd if you find some interesting (and valid - not \"interesting\" as in \n\"strange\", but \"interesting\" as in \"I'd be interested in actually doing \nthis) thing it gets wrong - usually by not understanding it and silently \njust doing some strange things - please holler.\n\nNow, as usual this certainly hasn't been getting a lot of testing. But my \ncode always works, no?\n\n\t\tLinus\n\n----\ndiff --git a/cache.h b/cache.h\nindex 677c6ac..dcfee1e 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -257,6 +257,7 @@ extern void *read_object_with_reference(\n const char *show_date(unsigned long time, int timezone);\n int parse_date(const char *date, char *buf, int bufsize);\n void datestamp(char *buf, int bufsize);\n+unsigned long approxidate(const char *);\n \n extern int setup_ident(void);\n extern char *get_ident(const char *name, const char *email, const char *date_str);\ndiff --git a/date.c b/date.c\nindex 63f5a09..73c063b 100644\n--- a/date.c\n+++ b/date.c\n@@ -5,6 +5,7 @@\n  */\n \n #include <time.h>\n+#include <sys/time.h>\n \n #include \"cache.h\"\n \n@@ -460,3 +461,126 @@ void datestamp(char *buf, int bufsize)\n \n \tdate_string(now, offset, buf, bufsize);\n }\n+\n+static void update_tm(struct tm *tm, unsigned long sec)\n+{\n+\ttime_t n = mktime(tm) - sec;\n+\tlocaltime_r(&n, tm);\n+}\n+\n+static const char *number_name[] = {\n+\t\"zero\", \"one\", \"two\", \"three\", \"four\",\n+\t\"five\", \"six\", \"seven\", \"eight\", \"nine\", \"ten\",\n+};\n+\n+static struct typelen {\n+\tconst char *type;\n+\tint length;\n+} typelen[] = {\n+\t{ \"seconds\", 1 },\n+\t{ \"minutes\", 60 },\n+\t{ \"hours\", 60*60 },\n+\t{ \"days\", 24*60*60 },\n+\t{ \"weeks\", 7*24*60*60 },\n+\t{ NULL }\n+};\t\n+\n+static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)\n+{\n+\tstruct typelen *tl;\n+\tconst char *end = date;\n+\tint n = 1, i;\n+\n+\twhile (isalpha(*++end))\n+\t\tn++;\n+\n+\tfor (i = 0; i < 12; i++) {\n+\t\tint match = match_string(date, month_names[i]);\n+\t\tif (match >= 3) {\n+\t\t\ttm->tm_mon = i;\n+\t\t\treturn end;\n+\t\t}\n+\t}\n+\n+\tif (match_string(date, \"yesterday\") > 8) {\n+\t\tupdate_tm(tm, 24*60*60);\n+\t\treturn end;\n+\t}\n+\n+\tif (!*num) {\n+\t\tfor (i = 1; i < 11; i++) {\n+\t\t\tint len = strlen(number_name[i]);\n+\t\t\tif (match_string(date, number_name[i]) == len) {\n+\t\t\t\t*num = i;\n+\t\t\t\treturn end;\n+\t\t\t}\n+\t\t}\n+\t\tif (match_string(date, \"last\") == 4)\n+\t\t\t*num = 1;\n+\t\treturn end;\n+\t}\n+\n+\ttl = typelen;\n+\twhile (tl->type) {\n+\t\tint len = strlen(tl->type);\n+\t\tif (match_string(date, tl->type) >= len-1) {\n+\t\t\tupdate_tm(tm, tl->length * *num);\n+\t\t\t*num = 0;\n+\t\t\treturn end;\n+\t\t}\n+\t\ttl++;\n+\t}\n+\n+\tif (match_string(date, \"months\") >= 5) {\n+\t\tint n = tm->tm_mon - *num;\n+\t\t*num = 0;\n+\t\twhile (n < 0) {\n+\t\t\tn += 12;\n+\t\t\ttm->tm_year--;\n+\t\t}\n+\t\ttm->tm_mon = n;\n+\t\treturn end;\n+\t}\n+\n+\tif (match_string(date, \"years\") >= 4) {\n+\t\ttm->tm_year -= *num;\n+\t\t*num = 0;\n+\t\treturn end;\n+\t}\n+\n+\treturn end;\n+}\n+\n+unsigned long approxidate(const char *date)\n+{\n+\tint number = 0;\n+\tstruct tm tm, now;\n+\tstruct timeval tv;\n+\tchar buffer[50];\n+\n+\tif (parse_date(date, buffer, sizeof(buffer)) > 0)\n+\t\treturn strtoul(buffer, NULL, 10);\n+\n+\tgettimeofday(&tv, NULL);\n+\tlocaltime_r(&tv.tv_sec, &tm);\n+\tnow = tm;\n+\tfor (;;) {\n+\t\tunsigned char c = *date;\n+\t\tif (!c)\n+\t\t\tbreak;\n+\t\tdate++;\n+\t\tif (isdigit(c)) {\n+\t\t\tchar *end;\n+\t\t\tnumber = strtoul(date-1, &end, 10);\n+\t\t\tdate = end;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (isalpha(c))\n+\t\t\tdate = approxidate_alpha(date-1, &tm, &number);\n+\t}\n+\tif (number > 0 && number < 32)\n+\t\ttm.tm_mday = number;\n+\tif (tm.tm_mon > now.tm_mon)\n+\t\ttm.tm_year--;\n+\treturn mktime(&tm);\n+}\ndiff --git a/rev-parse.c b/rev-parse.c\nindex 5a98982..bb4949a 100644\n--- a/rev-parse.c\n+++ b/rev-parse.c\n@@ -131,25 +131,12 @@ static int show_reference(const char *re\n \n static void show_datestring(const char *flag, const char *datestr)\n {\n-\tFILE *date;\n \tstatic char buffer[100];\n-\tstatic char cmd[1000];\n-\tint len;\n \n \t/* date handling requires both flags and revs */\n \tif ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))\n \t\treturn;\n-\tlen = strlen(flag);\n-\tmemcpy(buffer, flag, len);\n-\n-\tsnprintf(cmd, sizeof(cmd), \"date --date=%s +%%s\", sq_quote(datestr));\n-\tdate = popen(cmd, \"r\");\n-\tif (!date || !fgets(buffer + len, sizeof(buffer) - len, date))\n-\t\tdie(\"git-rev-list: bad date string\");\n-\tpclose(date);\n-\tlen = strlen(buffer);\n-\tif (buffer[len-1] == '\\n')\n-\t\tbuffer[--len] = 0;\n+\tsnprintf(buffer, sizeof(buffer), \"%s%lu\", flag, approxidate(datestr));\n \tshow(buffer);\n }\n \n"}]}