All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jeff King <peff@peff.net>
To: Junio C Hamano <gitster@pobox.com>
Cc: John Keeping <john@keeping.me.uk>, git@vger.kernel.org
Subject: Re: [RFC/PATCH] date: allow any format to display local time
Date: Mon, 31 Aug 2015 14:50:18 -0400	[thread overview]
Message-ID: <20150831185018.GA20555@sigill.intra.peff.net> (raw)
In-Reply-To: <xmqqtwrfweo7.fsf@gitster.mtv.corp.google.com>

On Mon, Aug 31, 2015 at 10:28:24AM -0700, Junio C Hamano wrote:

> This needs to be CC'ed to the author of a5481a6c (convert "enum
> date_mode" into a struct, 2015-06-25), which I just did.

Thanks. I'm definitely interested.

> I am unhappy with the change to blame.c, and that is not because I
> do not want "blame" to be touched.
> 
> The fact that this change has to touch it indicates that it is easy
> for other new callers of date formatting code to forget masking
> DATE_LOCAL bit like this patch does when they want to change their
> behaviour based on the settings of date-mode.  And it would be hard
> to catch such a subtle breakage during future reviews.

Yeah, my first instinct on seeing the bitfield was that it would
probably be much simpler to keep the enum pure, and add a bit to the
struct. A patch in that direction is below. I think the result is that
the using code is much cleaner, and the complexity of converting
"foo-local" into the enum/bitfield combination is contained in
parse_date_format. So for callers like blame:

> >  	/* The maximum width used to show the dates */
> > -	switch (blame_date_mode.type) {
> > +	switch (blame_date_mode.type & ~DATE_LOCAL) {
> >  	case DATE_RFC2822:
> >  		blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
> >  		break;

We can get rid of this hunk, which is good.

> > @@ -2600,7 +2600,6 @@ parse_done:
> >  		   fewer display columns. */
> >  		blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
> >  		break;
> > -	case DATE_LOCAL:
> >  	case DATE_NORMAL:
> >  		blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
> >  		break;

This hunk stays, because there is no more DATE_LOCAL. We could keep it
for historical compatibility, but the compiler will let us know if new
callers arrive.

There is one call-site in fast-import which uses DATE_MODE(LOCAL). In my
patch I convert it use a real "struct date_mode", as it is not much more
code, and is arguably more clear.

> I agree that among "enum date_mode_type", DATE_LOCAL is an oddball.
> It should be able to act as an orthogonal and independent flag with
> at least some, like NORMAL, SHORT, etc.  Specifying DATE_LOCAL with
> some others at the same time, however, would not make much sense,
> would it?  What does it even mean to say time as relative under
> DATE_LOCAL bit?

I think the "relative local" thing is not _too_ bad, as John's patch
enforces it at the user-level (it does not parse "relative-local" at
all, and mine similarly complains).

> >  	else
> >  		strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
> 
> What cannot be seen in the post-context of this hunk is that we
> deliberately drop the timezone information when tweaking the
> "normal" format to "local".  This is done only in the final else
> clause in show_date() because the code knows that LOCAL is not an
> independent bit.

I didn't address this in my patch (except to convert the check of
DATE_LOCAL to mode->local). I agree that we should handle it in other
formats, too, but I think it must be format-dependent. Certainly "rfc"
and "iso" must always show the timezone. I'd argue that "raw" probably
should, as well. That leaves only "short" and "relative", which do not
display the time zone in the first place. So all of the formats are
covered, I think.

> I think the original motivation is that there is no need to show the
> timezone information when the user knows the time is shown in the
> local timezone---after all, the --date=local option was given by her
> in the first place.  This kind of tweaking should be made consistent
> with other variants, now the other variants are interacting with
> this LOCAL bit.  If we were to go forward with this patch, I think
> we probably should remove this special casing of "showing normal
> date in localzone, drop the zone information", as we cannot sanely
> drop the TZ offset from the output of some of the formats and stay
> valid (e.g RFC2822).

I think I'd rather remain inconsistent between the formats (which, after
all, do not need to show exactly the same information), then have people
complain that "--date=local" is regressed and now shows a timezone.

> > @@ -777,14 +780,25 @@ void parse_date_format(const char *format, struct date_mode *mode)
> >  	else if (!strcmp(format, "iso8601") ||
> >  		 !strcmp(format, "iso"))
> >  		mode->type = DATE_ISO8601;
> > +	else if (!strcmp(format, "iso8601-local") ||
> > +		 !strcmp(format, "iso-local"))
> > +		mode->type = DATE_ISO8601_LOCAL;

I found the manual "-local" handling here to be a little, well...manual.
So in the patch below I've revamped the parsing to look left-to-right
for each component: type, local modifier, strftime format.

It ends up being about the same amount of code, but has two advantages:

  1. It's more flexible if we want to make more modifiers later. In
     fact, it would be trivial to implement the current "-strict" as a
     separate flag if we chose. I don't think there is much point in
     doing so, but we could do something like "default-local-strict"
     for showing the local time _with_ the timezone.

  2. We can provide more specific error messages (like "relative does
     not make sense with -local", rather than "unknown date format:
     relative-local").

But that is somewhat orthogonal to the enum thing. We could leave the
parsing as John has it, and just set mode->local as appropriate in each
conditional block.

---
 builtin/blame.c |  1 -
 cache.h         |  2 +-
 date.c          | 77 ++++++++++++++++++++++++++++++++++++++-------------------
 fast-import.c   |  6 ++++-
 4 files changed, 57 insertions(+), 29 deletions(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index 4db01c1..6fd1a63 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -2600,7 +2600,6 @@ parse_done:
 		   fewer display columns. */
 		blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
 		break;
-	case DATE_LOCAL:
 	case DATE_NORMAL:
 		blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
 		break;
diff --git a/cache.h b/cache.h
index 4e25271..9a91b1d 100644
--- a/cache.h
+++ b/cache.h
@@ -1091,7 +1091,6 @@ struct date_mode {
 		DATE_NORMAL = 0,
 		DATE_RELATIVE,
 		DATE_SHORT,
-		DATE_LOCAL,
 		DATE_ISO8601,
 		DATE_ISO8601_STRICT,
 		DATE_RFC2822,
@@ -1099,6 +1098,7 @@ struct date_mode {
 		DATE_RAW
 	} type;
 	const char *strftime_fmt;
+	int local;
 };
 
 /*
diff --git a/date.c b/date.c
index 8f91569..aa57cad 100644
--- a/date.c
+++ b/date.c
@@ -166,6 +166,7 @@ struct date_mode *date_mode_from_type(enum date_mode_type type)
 	if (type == DATE_STRFTIME)
 		die("BUG: cannot create anonymous strftime date_mode struct");
 	mode.type = type;
+	mode.local = 0;
 	return &mode;
 }
 
@@ -189,7 +190,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
 		return timebuf.buf;
 	}
 
-	if (mode->type == DATE_LOCAL)
+	if (mode->local)
 		tz = local_tzoffset(time);
 
 	tm = time_to_tm(time, tz);
@@ -232,7 +233,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
 				tm->tm_mday,
 				tm->tm_hour, tm->tm_min, tm->tm_sec,
 				tm->tm_year + 1900,
-				(mode->type == DATE_LOCAL) ? 0 : ' ',
+				mode->local ? 0 : ' ',
 				tz);
 	return timebuf.buf;
 }
@@ -770,32 +771,56 @@ int parse_date(const char *date, struct strbuf *result)
 	return 0;
 }
 
+static enum date_mode_type parse_date_type(const char *format, const char **end)
+{
+	if (skip_prefix(format, "relative", end))
+		return DATE_RELATIVE;
+	if (skip_prefix(format, "iso8601-strict", end) ||
+	    skip_prefix(format, "iso-strict", end))
+		return DATE_ISO8601_STRICT;
+	if (skip_prefix(format, "iso8601", end) ||
+	    skip_prefix(format, "iso", end))
+		return DATE_ISO8601;
+	if (skip_prefix(format, "rfc2822", end) ||
+	    skip_prefix(format, "rfc", end))
+		return DATE_RFC2822;
+	if (skip_prefix(format, "short", end))
+		return DATE_SHORT;
+	if (skip_prefix(format, "default", end))
+		return DATE_NORMAL;
+	if (skip_prefix(format, "raw", end))
+		return DATE_RAW;
+	if (skip_prefix(format, "format", end))
+		return DATE_STRFTIME;
+
+	die("unknown date format %s", format);
+}
+
 void parse_date_format(const char *format, struct date_mode *mode)
 {
-	if (!strcmp(format, "relative"))
-		mode->type = DATE_RELATIVE;
-	else if (!strcmp(format, "iso8601") ||
-		 !strcmp(format, "iso"))
-		mode->type = DATE_ISO8601;
-	else if (!strcmp(format, "iso8601-strict") ||
-		 !strcmp(format, "iso-strict"))
-		mode->type = DATE_ISO8601_STRICT;
-	else if (!strcmp(format, "rfc2822") ||
-		 !strcmp(format, "rfc"))
-		mode->type = DATE_RFC2822;
-	else if (!strcmp(format, "short"))
-		mode->type = DATE_SHORT;
-	else if (!strcmp(format, "local"))
-		mode->type = DATE_LOCAL;
-	else if (!strcmp(format, "default"))
-		mode->type = DATE_NORMAL;
-	else if (!strcmp(format, "raw"))
-		mode->type = DATE_RAW;
-	else if (skip_prefix(format, "format:", &format)) {
-		mode->type = DATE_STRFTIME;
-		mode->strftime_fmt = xstrdup(format);
-	} else
-		die("unknown date format %s", format);
+	const char *p;
+
+	/* historical alias */
+	if (!strcmp(format, "local"))
+		format = "default-local";
+
+	mode->type = parse_date_type(format, &p);
+	mode->local = 0;
+
+	if (skip_prefix(p, "-local", &p)) {
+		if (mode->type == DATE_RELATIVE)
+			die("relative-local date format is nonsensical");
+		mode->local = 1;
+	}
+
+	if (mode->type == DATE_STRFTIME) {
+		if (!skip_prefix(p, ":", &p))
+			die("date format missing colon separator: %s", format);
+		mode->strftime_fmt = xstrdup(p);
+	}
+
+	if (*p)
+		die("unknown date-mode modifier: %s", p);
 }
 
 void datestamp(struct strbuf *out)
diff --git a/fast-import.c b/fast-import.c
index 6c7c3c9..b19a1b5 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -412,6 +412,10 @@ static void write_crash_report(const char *err)
 	struct branch *b;
 	unsigned long lu;
 	struct recent_command *rc;
+	struct date_mode dm;
+
+	dm.type = DATE_NORMAL;
+	dm.local = 1;
 
 	if (!rpt) {
 		error("can't write crash report %s: %s", loc, strerror(errno));
@@ -424,7 +428,7 @@ static void write_crash_report(const char *err)
 	fprintf(rpt, "fast-import crash report:\n");
 	fprintf(rpt, "    fast-import process: %"PRIuMAX"\n", (uintmax_t) getpid());
 	fprintf(rpt, "    parent process     : %"PRIuMAX"\n", (uintmax_t) getppid());
-	fprintf(rpt, "    at %s\n", show_date(time(NULL), 0, DATE_MODE(LOCAL)));
+	fprintf(rpt, "    at %s\n", show_date(time(NULL), 0, &dm));
 	fputc('\n', rpt);
 
 	fputs("fatal: ", rpt);
-- 
2.5.1.739.g7891f6b

  reply	other threads:[~2015-08-31 18:50 UTC|newest]

Thread overview: 59+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-08-30 13:54 [RFC/PATCH] date: allow any format to display local time John Keeping
2015-08-31 17:28 ` Junio C Hamano
2015-08-31 18:50   ` Jeff King [this message]
2015-08-31 18:56     ` Jeff King
2015-08-31 19:57     ` Junio C Hamano
2015-08-31 20:00     ` John Keeping
2015-08-31 20:44       ` Jeff King
2015-08-31 20:47         ` [PATCH 1/2] fast-import: switch crash-report date to iso8601 Jeff King
2015-08-31 20:48         ` [PATCH 2/2] date: make "local" orthogonal to date format Jeff King
2015-08-31 21:27           ` John Keeping
2015-08-31 21:33             ` Jeff King
2015-08-31 22:05               ` Jeff King
2015-09-01  8:37                 ` John Keeping
2015-09-01 21:55                   ` [PATCH v2 0/6] Make " John Keeping
2015-09-01 21:55                     ` [PATCH v2 1/6] fast-import: switch crash-report date to iso8601 John Keeping
2015-09-01 21:55                     ` [PATCH v2 2/6] date: make "local" orthogonal to date format John Keeping
2015-09-01 22:16                       ` Junio C Hamano
2015-09-01 22:25                         ` Jeff King
2015-09-01 22:33                         ` John Keeping
2015-09-01 22:39                           ` Jeff King
2015-09-01 22:41                             ` Junio C Hamano
2015-09-02 17:36                       ` Junio C Hamano
2015-09-01 21:55                     ` [PATCH v2 3/6] t6300: introduce test_date() helper John Keeping
2015-09-01 22:19                       ` Junio C Hamano
2015-09-01 22:26                       ` Eric Sunshine
2015-09-01 22:31                       ` Jeff King
2015-09-01 22:40                         ` John Keeping
2015-09-01 22:41                           ` Jeff King
2015-09-01 21:55                     ` [PATCH v2 4/6] t6300: make UTC and local dates different John Keeping
2015-09-01 21:55                     ` [PATCH v2 5/6] t6300: add test for "raw" date format John Keeping
2015-09-01 21:55                     ` [PATCH v2 6/6] t6300: add tests for "-local" date formats John Keeping
2015-09-01 22:44                     ` [PATCH v2 0/6] Make "local" orthogonal to date format Jeff King
2015-09-02  7:48                       ` John Keeping
2015-09-02  8:05                         ` Jeff King
2015-09-02 15:16                           ` Junio C Hamano
2015-09-02 19:49                             ` John Keeping
2015-09-02 20:11                               ` Junio C Hamano
2015-09-02 20:21                                 ` John Keeping
2015-09-02 20:29                                   ` Junio C Hamano
2015-09-02 21:27                                     ` Jeff King
2015-09-03 21:48                     ` [PATCH v3 00/11] " John Keeping
2015-09-03 21:48                       ` [PATCH v3 01/11] Documentation/blame-options: don't list date formats John Keeping
2015-09-03 21:48                       ` [PATCH v3 02/11] Documentation/config: " John Keeping
2015-09-03 21:48                       ` [PATCH v3 03/11] Documentation/git-for-each-ref: " John Keeping
2015-09-03 21:48                       ` [PATCH v3 04/11] Documentation/rev-list: " John Keeping
2015-09-03 22:36                         ` Junio C Hamano
2015-09-03 21:48                       ` [PATCH v3 05/11] fast-import: switch crash-report date to iso8601 John Keeping
2015-09-03 21:48                       ` [PATCH v3 06/11] t6300: introduce test_date() helper John Keeping
2015-09-03 21:48                       ` [PATCH v3 07/11] t6300: add test for "raw" date format John Keeping
2015-09-03 21:48                       ` [PATCH v3 08/11] date: check for "local" before anything else John Keeping
2015-09-03 22:45                         ` Junio C Hamano
2015-09-03 21:48                       ` [PATCH v3 09/11] date: make "local" orthogonal to date format John Keeping
2015-09-03 21:49                       ` [PATCH v3 10/11] t6300: make UTC and local dates different John Keeping
2015-09-03 21:49                       ` [PATCH v3 11/11] t6300: add tests for "-local" date formats John Keeping
2015-09-08  7:53                       ` [PATCH v3 00/11] Make "local" orthogonal to date format Jeff King
2015-09-02 17:41           ` [PATCH 2/2] date: make " Junio C Hamano
2015-09-02 21:30             ` Jeff King
2015-09-02 22:07               ` John Keeping
2015-09-03 15:54                 ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20150831185018.GA20555@sigill.intra.peff.net \
    --to=peff@peff.net \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=john@keeping.me.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.