pretty: provide a strict ISO 8601 date format

Git's "ISO" date format does not really conform to the ISO 8601 standard due to small differences, and it cannot be parsed by ISO 8601-only parsers, e.g. those of XML toolchains. The output from "--date=iso" deviates from ISO 8601 in these ways: - a space instead of the `T` date/time delimiter - a space between time and time zone - no colon between hours and minutes of the time zone Add a strict ISO 8601 date format for displaying committer and author dates. Use the '%aI' and '%cI' format specifiers and add '--date=iso-strict' or '--date=iso8601-strict' date format names. See http://thread.gmane.org/gmane.comp.version-control.git/255879 and http://thread.gmane.org/gmane.comp.version-control.git/52414/focus=52585 for discussion. Signed-off-by: Beat Bolli <bbolli@ewanet.ch> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Beat Bolli committed Aug 29, 2014 at 18:58 UTC 466fb6742d7fb7d3e6994b2d0d8db83a8786ebcf
8 files changed +45 -7
Documentation/git-rev-list.txt
+1 -1
@@ -45,7 +45,7 @@ SYNOPSIS
45 [ \--regexp-ignore-case | -i ]
46 [ \--extended-regexp | -E ]
47 [ \--fixed-strings | -F ]
48 - [ \--date=(local|relative|default|iso|rfc|short) ]
48 + [ \--date=(local|relative|default|iso|iso-strict|rfc|short) ]
49 [ [\--objects | \--objects-edge] [ \--unpacked ] ]
50 [ \--pretty | \--header ]
51 [ \--bisect ]
Documentation/pretty-formats.txt
+4 -2
@@ -115,7 +115,8 @@ The placeholders are:
115 - '%aD': author date, RFC2822 style
116 - '%ar': author date, relative
117 - '%at': author date, UNIX timestamp
118 -- '%ai': author date, ISO 8601 format
118 +- '%ai': author date, ISO 8601-like format
119 +- '%aI': author date, strict ISO 8601 format
120 - '%cn': committer name
121 - '%cN': committer name (respecting .mailmap, see
122 linkgit:git-shortlog[1] or linkgit:git-blame[1])
@@ -126,7 +127,8 @@ The placeholders are:
127 - '%cD': committer date, RFC2822 style
128 - '%cr': committer date, relative
129 - '%ct': committer date, UNIX timestamp
129 -- '%ci': committer date, ISO 8601 format
130 +- '%ci': committer date, ISO 8601-like format
131 +- '%cI': committer date, strict ISO 8601 format
132 - '%d': ref names, like the --decorate option of linkgit:git-log[1]
133 - '%e': encoding
134 - '%s': subject
Documentation/rev-list-options.txt
+11 -2
@@ -677,7 +677,7 @@ include::pretty-options.txt[]
677 --relative-date::
678 Synonym for `--date=relative`.
679
680 ---date=(relative|local|default|iso|rfc|short|raw)::
680 +--date=(relative|local|default|iso|iso-strict|rfc|short|raw)::
681 Only takes effect for dates shown in human-readable format, such
682 as when using `--pretty`. `log.date` config variable sets a default
683 value for the log command's `--date` option.
@@ -687,7 +687,16 @@ e.g. ``2 hours ago''.
687 +
688 `--date=local` shows timestamps in user's local time zone.
689 +
690 -`--date=iso` (or `--date=iso8601`) shows timestamps in ISO 8601 format.
690 +`--date=iso` (or `--date=iso8601`) shows timestamps in a ISO 8601-like format.
691 +The differences to the strict ISO 8601 format are:
692 +
693 + - a space instead of the `T` date/time delimiter
694 + - a space between time and time zone
695 + - no colon between hours and minutes of the time zone
696 +
697 ++
698 +`--date=iso-strict` (or `--date=iso8601-strict`) shows timestamps in strict
699 +ISO 8601 format.
700 +
701 `--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822
702 format, often found in email messages.
builtin/blame.c
+3
@@ -2580,6 +2580,9 @@ parse_done:
2580 case DATE_RFC2822:
2581 blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
2582 break;
2583 + case DATE_ISO8601_STRICT:
2584 + blame_date_width = sizeof("2006-10-19T16:00:04-07:00");
2585 + break;
2586 case DATE_ISO8601:
2587 blame_date_width = sizeof("2006-10-19 16:00:04 -0700");
2588 break;
cache.h
+1
@@ -1037,6 +1037,7 @@ enum date_mode {
1037 DATE_SHORT,
1038 DATE_LOCAL,
1039 DATE_ISO8601,
1040 + DATE_ISO8601_STRICT,
1041 DATE_RFC2822,
1042 DATE_RAW
1043 };
date.c
+13 -1
@@ -200,7 +200,16 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
200 tm->tm_mday,
201 tm->tm_hour, tm->tm_min, tm->tm_sec,
202 tz);
203 - else if (mode == DATE_RFC2822)
203 + else if (mode == DATE_ISO8601_STRICT) {
204 + char sign = (tz >= 0) ? '+' : '-';
205 + tz = abs(tz);
206 + strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
207 + tm->tm_year + 1900,
208 + tm->tm_mon + 1,
209 + tm->tm_mday,
210 + tm->tm_hour, tm->tm_min, tm->tm_sec,
211 + sign, tz / 100, tz % 100);
212 + } else if (mode == DATE_RFC2822)
213 strbuf_addf(&timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
214 weekday_names[tm->tm_wday], tm->tm_mday,
215 month_names[tm->tm_mon], tm->tm_year + 1900,
@@ -751,6 +760,9 @@ enum date_mode parse_date_format(const char *format)
760 else if (!strcmp(format, "iso8601") ||
761 !strcmp(format, "iso"))
762 return DATE_ISO8601;
763 + else if (!strcmp(format, "iso8601-strict") ||
764 + !strcmp(format, "iso-strict"))
765 + return DATE_ISO8601_STRICT;
766 else if (!strcmp(format, "rfc2822") ||
767 !strcmp(format, "rfc"))
768 return DATE_RFC2822;
pretty.c
+4 -1
@@ -731,9 +731,12 @@ static size_t format_person_part(struct strbuf *sb, char part,
731 case 'r': /* date, relative */
732 strbuf_addstr(sb, show_ident_date(&s, DATE_RELATIVE));
733 return placeholder_len;
734 - case 'i': /* date, ISO 8601 */
734 + case 'i': /* date, ISO 8601-like */
735 strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601));
736 return placeholder_len;
737 + case 'I': /* date, ISO 8601 strict */
738 + strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601_STRICT));
739 + return placeholder_len;
740 }
741
742 skip:
t/t4205-log-pretty-formats.sh
+8
@@ -431,6 +431,14 @@ EOF
431 test_cmp expected actual
432 '
433
434 +# ISO strict date format
435 +test_expect_success 'ISO and ISO-strict date formats display the same values' '
436 + git log --format=%ai%n%ci |
437 + sed -e "s/ /T/; s/ //; s/..\$/:&/" >expected &&
438 + git log --format=%aI%n%cI >actual &&
439 + test_cmp expected actual
440 +'
441 +
442 # get new digests (with no abbreviations)
443 head1=$(git rev-parse --verify HEAD~0) &&
444 head2=$(git rev-parse --verify HEAD~1) &&