date: make "local" orthogonal to date format

Most of our "--date" modes are about the format of the date: which items we show and in what order. But "--date=local" is a bit of an oddball. It means "show the date in the normal format, but using the local timezone". The timezone we use is orthogonal to the actual format, and there is no reason we could not have "localized iso8601", etc. This patch adds a "local" boolean field to "struct date_mode", and drops the DATE_LOCAL element from the date_mode_type enum (it's now just DATE_NORMAL plus local=1). The new feature is accessible to users by adding "-local" to any date mode (e.g., "iso-local"), and we retain "local" as an alias for "default-local" for backwards compatibility. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: John Keeping <john@keeping.me.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Sep 3, 2015 at 22:48 UTC add00ba2de971e0c5ba00f1f02b73c5534079d2c
4 files changed +61 -33
Documentation/rev-list-options.txt
+15 -6
@@ -702,12 +702,16 @@ include::pretty-options.txt[]
702 --date=<format>::
703 Only takes effect for dates shown in human-readable format, such
704 as when using `--pretty`. `log.date` config variable sets a default
705 - value for the log command's `--date` option.
705 + value for the log command's `--date` option. By default, dates
706 + are shown in the original time zone (either committer's or
707 + author's). If `-local` is appended to the format (e.g.,
708 + `iso-local`), the user's local time zone is used instead.
709 +
710 `--date=relative` shows dates relative to the current time,
708 -e.g. ``2 hours ago''.
711 +e.g. ``2 hours ago''. The `-local` option cannot be used with
712 +`--raw` or `--relative`.
713 +
710 -`--date=local` shows timestamps in user's local time zone.
714 +`--date=local` is an alias for `--date=default-local`.
715 +
716 `--date=iso` (or `--date=iso8601`) shows timestamps in a ISO 8601-like format.
717 The differences to the strict ISO 8601 format are:
@@ -730,10 +734,15 @@ format, often found in email messages.
734 `--date=format:...` feeds the format `...` to your system `strftime`.
735 Use `--date=format:%c` to show the date in your system locale's
736 preferred format. See the `strftime` manual for a complete list of
733 -format placeholders.
737 +format placeholders. When using `-local`, the correct syntax is
738 +`--date=format-local:...`.
739 +
735 -`--date=default` shows timestamps in the original time zone
736 -(either committer's or author's).
740 +`--date=default` is the default format, and is similar to
741 +`--date=rfc2822`, with a few exceptions:
742 +
743 + - there is no comma after the day-of-week
744 +
745 + - the time zone is omitted when the local time zone is used
746
747 ifdef::git-rev-list[]
748 --header::
builtin/blame.c
-1
@@ -2600,7 +2600,6 @@ parse_done:
2600 fewer display columns. */
2601 blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
2602 break;
2603 - case DATE_LOCAL:
2603 case DATE_NORMAL:
2604 blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
2605 break;
cache.h
+1 -1
@@ -1110,7 +1110,6 @@ struct date_mode {
1110 DATE_NORMAL = 0,
1111 DATE_RELATIVE,
1112 DATE_SHORT,
1113 - DATE_LOCAL,
1113 DATE_ISO8601,
1114 DATE_ISO8601_STRICT,
1115 DATE_RFC2822,
@@ -1118,6 +1117,7 @@ struct date_mode {
1117 DATE_RAW
1118 } type;
1119 const char *strftime_fmt;
1120 + int local;
1121 };
1122
1123 /*
date.c
+45 -25
@@ -166,6 +166,7 @@ struct date_mode *date_mode_from_type(enum date_mode_type type)
166 if (type == DATE_STRFTIME)
167 die("BUG: cannot create anonymous strftime date_mode struct");
168 mode.type = type;
169 + mode.local = 0;
170 return &mode;
171 }
172
@@ -174,7 +175,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
175 struct tm *tm;
176 static struct strbuf timebuf = STRBUF_INIT;
177
177 - if (mode->type == DATE_LOCAL)
178 + if (mode->local)
179 tz = local_tzoffset(time);
180
181 if (mode->type == DATE_RAW) {
@@ -232,7 +233,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
233 tm->tm_mday,
234 tm->tm_hour, tm->tm_min, tm->tm_sec,
235 tm->tm_year + 1900,
235 - (mode->type == DATE_LOCAL) ? 0 : ' ',
236 + mode->local ? 0 : ' ',
237 tz);
238 return timebuf.buf;
239 }
@@ -770,31 +771,50 @@ int parse_date(const char *date, struct strbuf *result)
771 return 0;
772 }
773
774 +static enum date_mode_type parse_date_type(const char *format, const char **end)
775 +{
776 + if (skip_prefix(format, "relative", end))
777 + return DATE_RELATIVE;
778 + if (skip_prefix(format, "iso8601-strict", end) ||
779 + skip_prefix(format, "iso-strict", end))
780 + return DATE_ISO8601_STRICT;
781 + if (skip_prefix(format, "iso8601", end) ||
782 + skip_prefix(format, "iso", end))
783 + return DATE_ISO8601;
784 + if (skip_prefix(format, "rfc2822", end) ||
785 + skip_prefix(format, "rfc", end))
786 + return DATE_RFC2822;
787 + if (skip_prefix(format, "short", end))
788 + return DATE_SHORT;
789 + if (skip_prefix(format, "default", end))
790 + return DATE_NORMAL;
791 + if (skip_prefix(format, "raw", end))
792 + return DATE_RAW;
793 + if (skip_prefix(format, "format", end))
794 + return DATE_STRFTIME;
795 +
796 + die("unknown date format %s", format);
797 +}
798 +
799 void parse_date_format(const char *format, struct date_mode *mode)
800 {
775 - if (!strcmp(format, "relative"))
776 - mode->type = DATE_RELATIVE;
777 - else if (!strcmp(format, "iso8601") ||
778 - !strcmp(format, "iso"))
779 - mode->type = DATE_ISO8601;
780 - else if (!strcmp(format, "iso8601-strict") ||
781 - !strcmp(format, "iso-strict"))
782 - mode->type = DATE_ISO8601_STRICT;
783 - else if (!strcmp(format, "rfc2822") ||
784 - !strcmp(format, "rfc"))
785 - mode->type = DATE_RFC2822;
786 - else if (!strcmp(format, "short"))
787 - mode->type = DATE_SHORT;
788 - else if (!strcmp(format, "local"))
789 - mode->type = DATE_LOCAL;
790 - else if (!strcmp(format, "default"))
791 - mode->type = DATE_NORMAL;
792 - else if (!strcmp(format, "raw"))
793 - mode->type = DATE_RAW;
794 - else if (skip_prefix(format, "format:", &format)) {
795 - mode->type = DATE_STRFTIME;
796 - mode->strftime_fmt = xstrdup(format);
797 - } else
801 + const char *p;
802 +
803 + /* historical alias */
804 + if (!strcmp(format, "local"))
805 + format = "default-local";
806 +
807 + mode->type = parse_date_type(format, &p);
808 + mode->local = 0;
809 +
810 + if (skip_prefix(p, "-local", &p))
811 + mode->local = 1;
812 +
813 + if (mode->type == DATE_STRFTIME) {
814 + if (!skip_prefix(p, ":", &p))
815 + die("date format missing colon separator: %s", format);
816 + mode->strftime_fmt = xstrdup(p);
817 + } else if (*p)
818 die("unknown date format %s", format);
819 }
820