From ad628fc6a4f92f34cd47520b9103541075266fc8 Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Sat, 15 Jul 2017 12:07:04 -0400 Subject: [PATCH 1/3] Fix #522: Increase size of the internal date buffer --- NEWS.adoc | 1 + src/util.c | 2 +- test/main/date-test | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/NEWS.adoc b/NEWS.adoc index 7b5114273..9197a4c2f 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -55,6 +55,7 @@ Bug fixes: - Fix reading from stdin for `tig show`. - Document problem of outdated system-wide `tigrc` files in Homebrew. (GH #598) - Repaint the display when toggling `line-graphics`. (GH #527) + - Fix custom date formatting support longer strings. (GH #522) tig-2.2.2 --------- diff --git a/src/util.c b/src/util.c index 728488690..7f4f19e6f 100644 --- a/src/util.c +++ b/src/util.c @@ -180,7 +180,7 @@ get_relative_date(const struct time *time, char *buf, size_t buflen, bool compac const char * mkdate(const struct time *time, enum date date, bool local, const char *custom_format) { - static char buf[STRING_SIZE("2006-04-29 14:21") + 1]; + static char buf[SIZEOF_STR]; struct tm tm; const char *format; diff --git a/test/main/date-test b/test/main/date-test index f7ceae219..d7cdfde5b 100755 --- a/test/main/date-test +++ b/test/main/date-test @@ -28,6 +28,11 @@ steps ' :toggle date :save-display no-date.screen + + :set main-view-date = custom + :set main-view-date-local = true + :set main-view-date-format = "%F %R %Z" + :save-display custom-date.screen ' test_tig --pretty=raw < "$source_dir/$test.in" @@ -205,3 +210,32 @@ Jonas Fonseca Only split the tree view when the tree view is visible Jonas Fonseca Initial commit [main] 91912eb97da4f6907015dab41ef9bba315730854 - commit 1 of 25 100% EOF + +assert_equals 'custom-date.screen' < Date: Sat, 15 Jul 2017 14:41:49 -0400 Subject: [PATCH 2/3] Support time zone offsets (%z) in custom dates --- NEWS.adoc | 3 ++- doc/tigrc.5.adoc | 8 ++++--- src/util.c | 54 ++++++++++++++++++++++++++++++++++++++++----- test/main/date-test | 33 +++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 10 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index 9197a4c2f..3f0ae7d3b 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -55,7 +55,8 @@ Bug fixes: - Fix reading from stdin for `tig show`. - Document problem of outdated system-wide `tigrc` files in Homebrew. (GH #598) - Repaint the display when toggling `line-graphics`. (GH #527) - - Fix custom date formatting support longer strings. (GH #522) + - Fix custom date formatting to support longer strings and the time zone offset + specifier ("%z"). (GH #522) tig-2.2.2 --------- diff --git a/doc/tigrc.5.adoc b/doc/tigrc.5.adoc index fc38ffb56..81e19ea11 100644 --- a/doc/tigrc.5.adoc +++ b/doc/tigrc.5.adoc @@ -453,11 +453,13 @@ date:: How to display dates. If set to "relative" or "relative-compact" a relative date will be used, e.g. "2 minutes ago" or "2m". If set to "custom", the strftime(3) string format specified in the "format" - option is used. + option is used. - 'local' (bool): If true, use localtime(3) to convert to local timezone. Note that relative dates always use local offsets. - - 'format' (string): format string to pass to strftime(3) when 'custom' - display mode has been selected. + - 'format' (string): custom date format string to pass to strftime(3) + when 'custom' display mode has been selected. For time zones, "%z" is + supported whereas "%Z" will always show "UTC", since Git only stores + time zone offsets. - 'width' (int): Width of the column. When set to zero, the width is automatically sized to fit the content. diff --git a/src/util.c b/src/util.c index 7f4f19e6f..00f8de61a 100644 --- a/src/util.c +++ b/src/util.c @@ -183,6 +183,9 @@ mkdate(const struct time *time, enum date date, bool local, const char *custom_f static char buf[SIZEOF_STR]; struct tm tm; const char *format; + bool tz_restore = false; + char *tzdup = NULL; + bool ok; if (!date || !time || !time->sec) return ""; @@ -191,18 +194,57 @@ mkdate(const struct time *time, enum date date, bool local, const char *custom_f return get_relative_date(time, buf, sizeof(buf), date == DATE_RELATIVE_COMPACT); + format = date != DATE_CUSTOM + ? "%Y-%m-%d %H:%M" + : custom_format ? custom_format : "%Y-%m-%d"; + if (local) { time_t date = time->sec + time->tz; + localtime_r(&date, &tm); - } - else { + + } else { + /* Format dates with time zones by temporarily setting + * the TZ environment variable and calling tzset(3) so + * gmtime and strftime has the proper time zone info. + * NOTE: Only works for %z (ie. formatting time zones as + * offset +0200) since Git only records offsets in the + * ident dates. */ + if (format == custom_format) { + const char *tz = getenv("TZ"); + char tzbuf[20] = ""; + + tzdup = tz ? strdup(tz) : NULL; + if (tz && !tzdup) + return ""; + + if (!string_format(tzbuf, "UTC%+03d:%02d", + (time->tz / 60 / 60), (time->tz / 60 % 60))) { + free(tzdup); + return ""; + } + + tz_restore = true; + setenv("TZ", tzbuf, 1); + tzset(); + } + gmtime_r(&time->sec, &tm); } - format = date != DATE_CUSTOM - ? "%Y-%m-%d %H:%M" - : custom_format ? custom_format : "%Y-%m-%d"; - return strftime(buf, sizeof(buf), format, &tm) ? buf : NULL; + ok = !!strftime(buf, sizeof(buf), format, &tm); + + if (tz_restore) { + if (tzdup) { + setenv("TZ", tzdup, 1); + free(tzdup); + } else { + unsetenv("TZ"); + } + tzset(); + } + + return ok ? buf : NULL; } const char * diff --git a/test/main/date-test b/test/main/date-test index d7cdfde5b..7939dccaf 100755 --- a/test/main/date-test +++ b/test/main/date-test @@ -33,6 +33,10 @@ steps ' :set main-view-date-local = true :set main-view-date-format = "%F %R %Z" :save-display custom-date.screen + + :set main-view-date-local = false + :set main-view-date-format = "%F %R %z" + :save-display custom-date-non-local.screen ' test_tig --pretty=raw < "$source_dir/$test.in" @@ -239,3 +243,32 @@ assert_equals 'custom-date.screen' < Date: Sun, 16 Jul 2017 10:07:54 -0400 Subject: [PATCH 3/3] Use struct tm.tm_gmtoff --- configure.ac | 2 ++ src/util.c | 43 ++++++++++--------------------------------- 2 files changed, 12 insertions(+), 33 deletions(-) diff --git a/configure.ac b/configure.ac index 8b956baf2..942d85199 100644 --- a/configure.ac +++ b/configure.ac @@ -21,6 +21,8 @@ AC_CHECK_FUNCS([mkstemps], [AC_SUBST([NO_MKSTEMPS], ["#"])]) AC_CHECK_FUNCS([setenv], [AC_SUBST([NO_SETENV], ["#"])]) AC_CHECK_FUNCS([strndup], [AC_SUBST([NO_STRNDUP], ["#"])]) +AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[#include ]) + AX_WITH_CURSES case "$ax_cv_ncurses" in "no") AC_MSG_ERROR([ncurses not found]) diff --git a/src/util.c b/src/util.c index 00f8de61a..5885ef5ba 100644 --- a/src/util.c +++ b/src/util.c @@ -183,9 +183,7 @@ mkdate(const struct time *time, enum date date, bool local, const char *custom_f static char buf[SIZEOF_STR]; struct tm tm; const char *format; - bool tz_restore = false; - char *tzdup = NULL; - bool ok; + char tzbuf[20] = ""; if (!date || !time || !time->sec) return ""; @@ -198,12 +196,15 @@ mkdate(const struct time *time, enum date date, bool local, const char *custom_f ? "%Y-%m-%d %H:%M" : custom_format ? custom_format : "%Y-%m-%d"; + tzset(); if (local) { time_t date = time->sec + time->tz; - localtime_r(&date, &tm); } else { + gmtime_r(&time->sec, &tm); + +#if HAVE_STRUCT_TM_TM_GMTOFF /* Format dates with time zones by temporarily setting * the TZ environment variable and calling tzset(3) so * gmtime and strftime has the proper time zone info. @@ -211,40 +212,16 @@ mkdate(const struct time *time, enum date date, bool local, const char *custom_f * offset +0200) since Git only records offsets in the * ident dates. */ if (format == custom_format) { - const char *tz = getenv("TZ"); - char tzbuf[20] = ""; - - tzdup = tz ? strdup(tz) : NULL; - if (tz && !tzdup) - return ""; - if (!string_format(tzbuf, "UTC%+03d:%02d", - (time->tz / 60 / 60), (time->tz / 60 % 60))) { - free(tzdup); + (time->tz / 60 / 60), (time->tz / 60 % 60))) return ""; - } - - tz_restore = true; - setenv("TZ", tzbuf, 1); - tzset(); - } - - gmtime_r(&time->sec, &tm); - } - - ok = !!strftime(buf, sizeof(buf), format, &tm); - - if (tz_restore) { - if (tzdup) { - setenv("TZ", tzdup, 1); - free(tzdup); - } else { - unsetenv("TZ"); + tm.tm_gmtoff = time->tz; + tm.tm_zone = tzbuf; } - tzset(); +#endif } - return ok ? buf : NULL; + return strftime(buf, sizeof(buf), format, &tm) ? buf : NULL; } const char *