git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* git's rev-parse.c function show_datestring presumes gnu date
@ 2005-11-14 23:02 Randal L. Schwartz
  2005-11-14 23:08 ` Linus Torvalds
  0 siblings, 1 reply; 3+ messages in thread
From: Randal L. Schwartz @ 2005-11-14 23:02 UTC (permalink / raw)
  To: git


git-rev-parse calls GNU date externally for --since, --after, --before,
and --until, which will fail on at least OpenBSD and Mac OSX (Darwin).

At a minimum, this code can be commented out on such platforms.  Better
would be to have some sort of suitable replacement.

Perhaps the Makefile can test if "date" is GNU date as well.

-- 
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/security consulting, Technical writing, Comedy, etc. etc.
See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: git's rev-parse.c function show_datestring presumes gnu date
  2005-11-14 23:02 git's rev-parse.c function show_datestring presumes gnu date Randal L. Schwartz
@ 2005-11-14 23:08 ` Linus Torvalds
  2005-11-15  3:29   ` Linus Torvalds
  0 siblings, 1 reply; 3+ messages in thread
From: Linus Torvalds @ 2005-11-14 23:08 UTC (permalink / raw)
  To: Randal L. Schwartz; +Cc: git



On Mon, 14 Nov 2005, Randal L. Schwartz wrote:
> 
> git-rev-parse calls GNU date externally for --since, --after, --before,
> and --until, which will fail on at least OpenBSD and Mac OSX (Darwin).
> 
> At a minimum, this code can be commented out on such platforms.  Better
> would be to have some sort of suitable replacement.

I really wanted to do the date parsing myself, rather than call out to 
date, but I got lazy.

It shouldn't be all that difficult to do some trivial cases (gnu date 
handles totally insane things, we could easily do with just a simpler 
thing). 

Anybody want to handle
 - partial real dates ("August 5th" - figure out the year automatically, 
   and notice that it might be _last_ year since the date only makes 
   sense if it's in the past)
 - relative dates ("5 days ago", "yesterday", "one week ago")

It shouldn't be that hard, and if you do _not_ touch the date.c functions 
(which have to be totally dependable) you can be pretty damn lazy and lax 
and just guess a lot.

		Linus

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: git's rev-parse.c function show_datestring presumes gnu date
  2005-11-14 23:08 ` Linus Torvalds
@ 2005-11-15  3:29   ` Linus Torvalds
  0 siblings, 0 replies; 3+ messages in thread
From: Linus Torvalds @ 2005-11-15  3:29 UTC (permalink / raw)
  To: Randal L. Schwartz, Junio C Hamano; +Cc: git



On Mon, 14 Nov 2005, Linus Torvalds wrote:
> 
> It shouldn't be all that difficult to do some trivial cases (gnu date 
> handles totally insane things, we could easily do with just a simpler 
> thing). 

Ok. This is the insane patch to do this.

It really isn't very careful, and the reason I call it "approxidate()" 
will become obvious when you look at the code. It is very liberal in what 
it accepts, to the point where sometimes the results may not make a whole 
lot of sense.

It accepts "last week" as a date string, by virtue of "last" parsing as 
the number 1, and it totally ignoring superfluous fluff like "ago", so 
"last week" ends up being exactly the same thing as "1 week ago". Fine so 
far.

It has strange side effects: "last december" will actually parse as "Dec 
1", which actually _does_ turn out right, because it will then notice that 
it's not December yet, so it will decide that you must be talking about a 
date last year. So it actually gets it right, but it's kind of for the 
"wrong" reasons.

It also accepts the numbers 1..10 in string format ("one" .. "ten"), so 
you can do "ten weeks ago" or "ten hours ago" and it will do the right 
thing.

But it will do some really strange thigns too: the string "this will last 
forever", will not recognize anyting but "last", which is recognized as 
"1", which since it doesn't understand anything else it will think is the 
day of the month. So if you do

	gitk --since="this will last forever"

the date will actually parse as the first day of the current month.

And it will parse the string "now" as "now", but only because it doesn't 
understand it at all, and it makes everything relative to "now".

Similarly, it doesn't actually parse the "ago" or "from now", so "2 weeks 
ago" is exactly the same as "2 weeks from now". It's the current date 
minus 14 days.

But hey, it's probably better (and certainly faster) than depending on GNU 
date. So now you can portably do things like

	gitk --since="two weeks and three days ago"
	git log --since="July 5"
	git-whatchanged --since="10 hours ago"
	git log --since="last october"

and it will actually do exactly what you thought it would do (I think). It 
will count 17 days backwards, and it will do so even if you don't have GNU 
date installed.

(I don't do "last monday" or similar yet, but I can extend it to that too 
if people want).

It was kind of fun trying to write code that uses such totally relaxed 
"understanding" of dates yet tries to get it right for the trivial cases. 
The result should be mixed with a few strange preprocessor tricks, and be 
submitted for the IOCCC ;)

Feel free to try it out, and see how many strange dates it gets right. Or 
wrong.

And if you find some interesting (and valid - not "interesting" as in 
"strange", but "interesting" as in "I'd be interested in actually doing 
this) thing it gets wrong - usually by not understanding it and silently 
just doing some strange things - please holler.

Now, as usual this certainly hasn't been getting a lot of testing. But my 
code always works, no?

		Linus

----
diff --git a/cache.h b/cache.h
index 677c6ac..dcfee1e 100644
--- a/cache.h
+++ b/cache.h
@@ -257,6 +257,7 @@ extern void *read_object_with_reference(
 const char *show_date(unsigned long time, int timezone);
 int parse_date(const char *date, char *buf, int bufsize);
 void datestamp(char *buf, int bufsize);
+unsigned long approxidate(const char *);
 
 extern int setup_ident(void);
 extern char *get_ident(const char *name, const char *email, const char *date_str);
diff --git a/date.c b/date.c
index 63f5a09..73c063b 100644
--- a/date.c
+++ b/date.c
@@ -5,6 +5,7 @@
  */
 
 #include <time.h>
+#include <sys/time.h>
 
 #include "cache.h"
 
@@ -460,3 +461,126 @@ void datestamp(char *buf, int bufsize)
 
 	date_string(now, offset, buf, bufsize);
 }
+
+static void update_tm(struct tm *tm, unsigned long sec)
+{
+	time_t n = mktime(tm) - sec;
+	localtime_r(&n, tm);
+}
+
+static const char *number_name[] = {
+	"zero", "one", "two", "three", "four",
+	"five", "six", "seven", "eight", "nine", "ten",
+};
+
+static struct typelen {
+	const char *type;
+	int length;
+} typelen[] = {
+	{ "seconds", 1 },
+	{ "minutes", 60 },
+	{ "hours", 60*60 },
+	{ "days", 24*60*60 },
+	{ "weeks", 7*24*60*60 },
+	{ NULL }
+};	
+
+static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
+{
+	struct typelen *tl;
+	const char *end = date;
+	int n = 1, i;
+
+	while (isalpha(*++end))
+		n++;
+
+	for (i = 0; i < 12; i++) {
+		int match = match_string(date, month_names[i]);
+		if (match >= 3) {
+			tm->tm_mon = i;
+			return end;
+		}
+	}
+
+	if (match_string(date, "yesterday") > 8) {
+		update_tm(tm, 24*60*60);
+		return end;
+	}
+
+	if (!*num) {
+		for (i = 1; i < 11; i++) {
+			int len = strlen(number_name[i]);
+			if (match_string(date, number_name[i]) == len) {
+				*num = i;
+				return end;
+			}
+		}
+		if (match_string(date, "last") == 4)
+			*num = 1;
+		return end;
+	}
+
+	tl = typelen;
+	while (tl->type) {
+		int len = strlen(tl->type);
+		if (match_string(date, tl->type) >= len-1) {
+			update_tm(tm, tl->length * *num);
+			*num = 0;
+			return end;
+		}
+		tl++;
+	}
+
+	if (match_string(date, "months") >= 5) {
+		int n = tm->tm_mon - *num;
+		*num = 0;
+		while (n < 0) {
+			n += 12;
+			tm->tm_year--;
+		}
+		tm->tm_mon = n;
+		return end;
+	}
+
+	if (match_string(date, "years") >= 4) {
+		tm->tm_year -= *num;
+		*num = 0;
+		return end;
+	}
+
+	return end;
+}
+
+unsigned long approxidate(const char *date)
+{
+	int number = 0;
+	struct tm tm, now;
+	struct timeval tv;
+	char buffer[50];
+
+	if (parse_date(date, buffer, sizeof(buffer)) > 0)
+		return strtoul(buffer, NULL, 10);
+
+	gettimeofday(&tv, NULL);
+	localtime_r(&tv.tv_sec, &tm);
+	now = tm;
+	for (;;) {
+		unsigned char c = *date;
+		if (!c)
+			break;
+		date++;
+		if (isdigit(c)) {
+			char *end;
+			number = strtoul(date-1, &end, 10);
+			date = end;
+			continue;
+		}
+		if (isalpha(c))
+			date = approxidate_alpha(date-1, &tm, &number);
+	}
+	if (number > 0 && number < 32)
+		tm.tm_mday = number;
+	if (tm.tm_mon > now.tm_mon)
+		tm.tm_year--;
+	return mktime(&tm);
+}
diff --git a/rev-parse.c b/rev-parse.c
index 5a98982..bb4949a 100644
--- a/rev-parse.c
+++ b/rev-parse.c
@@ -131,25 +131,12 @@ static int show_reference(const char *re
 
 static void show_datestring(const char *flag, const char *datestr)
 {
-	FILE *date;
 	static char buffer[100];
-	static char cmd[1000];
-	int len;
 
 	/* date handling requires both flags and revs */
 	if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
 		return;
-	len = strlen(flag);
-	memcpy(buffer, flag, len);
-
-	snprintf(cmd, sizeof(cmd), "date --date=%s +%%s", sq_quote(datestr));
-	date = popen(cmd, "r");
-	if (!date || !fgets(buffer + len, sizeof(buffer) - len, date))
-		die("git-rev-list: bad date string");
-	pclose(date);
-	len = strlen(buffer);
-	if (buffer[len-1] == '\n')
-		buffer[--len] = 0;
+	snprintf(buffer, sizeof(buffer), "%s%lu", flag, approxidate(datestr));
 	show(buffer);
 }
 

^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2005-11-15  3:30 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-11-14 23:02 git's rev-parse.c function show_datestring presumes gnu date Randal L. Schwartz
2005-11-14 23:08 ` Linus Torvalds
2005-11-15  3:29   ` Linus Torvalds

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).