From 8483129b6a4746247fd7fb3b5554e10181ad937a Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 10 Jun 2020 13:26:27 +0300 Subject: Add workaround for MinGW GCC bug so to_stream(timestamp) properly handles %e specifier --- libbutl/timestamp.cxx | 67 +++++++++++++++++++++++++++++++++++----------- libbutl/timestamp.mxx | 2 +- tests/timestamp/driver.cxx | 3 +++ 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/libbutl/timestamp.cxx b/libbutl/timestamp.cxx index 0242758..9be2a82 100644 --- a/libbutl/timestamp.cxx +++ b/libbutl/timestamp.cxx @@ -300,24 +300,39 @@ namespace butl // those that we handle ourselves. Watch out for the escapes (%%). // size_t i (0), j (0); // put_time()'s range. + + // Print the time partially, using the not printed part of the format + // string up to the current scanning position and setting the current + // character to NULL. Return true if the operation succeeded. + // + auto print = [&i, &j, &fmt, &os, &tm] () + { + if (i != j) + { + fmt[j] = '\0'; // Note: there is no harm if j == n. + if (!(os << put_time (&tm, fmt + i))) + return false; + } + + return true; + }; + for (; j != n; ++j) { if (fmt[j] == '%' && j + 1 != n) { - if (fmt[j + 1] == '[') + char c (fmt[j + 1]); + + if (c == '[') { if (os.width () != 0) throw runtime_error ( "padding is not supported when printing nanoseconds"); - // Our fragment. First see if we need to call put_time(). + // Our fragment. // - if (i != j) - { - fmt[j] = '\0'; - if (!(os << put_time (&tm, fmt + i))) - return os; - } + if (!print ()) + return os; j += 2; // Character after '['. if (j == n) @@ -348,19 +363,39 @@ namespace butl i = j + 1; // j is incremented in the for-loop header. } + // + // Note that MinGW (as of GCC 9.2) libstdc++'s implementations of + // std::put_time() and std::strftime() don't recognize the %e + // specifier, converting it into the empty string (bug #95624). Thus, + // we handle this specifier ourselves for libstdc++ on Windows. + // +#if defined(_WIN32) && defined(__GLIBCXX__) + else if (c == 'e') + { + if (!print ()) + return os; + + ostream::fmtflags fl (os.flags ()); + char fc (os.fill (' ')); + + // Note: the width is automatically reset to 0 (unspecified) after + // we print the day. + // + os << dec << right << setw (2) << tm.tm_mday; + + os.fill (fc); + os.flags (fl); + + ++j; // Positions at 'e'. + i = j + 1; // j is incremented in the for-loop header. + } +#endif else ++j; // Skip % and the next character to handle %%. } } - // Do we need to call put_time() one last time? - // - if (i != j) - { - if (!(os << put_time (&tm, fmt + i))) - return os; - } - + print (); // Call put_time() one last time, if required. return os; } diff --git a/libbutl/timestamp.mxx b/libbutl/timestamp.mxx index 9525ec0..141e13d 100644 --- a/libbutl/timestamp.mxx +++ b/libbutl/timestamp.mxx @@ -79,7 +79,7 @@ LIBBUTL_MODEXPORT namespace butl // Print human-readable representation of the timestamp. // - // By default the timestamp is printed by localtime_r() in the local + // By default the timestamp is converted by localtime_r() to the local // timezone, so tzset() from should be called prior to using the // corresponding operator or the to_stream() function (normally from main() // or equivalent). diff --git a/tests/timestamp/driver.cxx b/tests/timestamp/driver.cxx index 11e1821..6283798 100644 --- a/tests/timestamp/driver.cxx +++ b/tests/timestamp/driver.cxx @@ -199,6 +199,9 @@ main () "%[.N]%Y-%m-%d %H:%M:%S", "." + ns (384902285) + "2016-02-21 19:31:10")); + assert (parse ("Feb 1 2016", "%b %e %Y", "Feb 1 2016")); + assert (parse ("Feb 11 2016", "%b %e %Y", "Feb 11 2016")); + // setlocale (LC_ALL, ""); // setlocale (LC_ALL, "de_DE.utf-8"); // setlocale (LC_ALL, "de-de"); -- cgit v1.1