aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2020-06-10 13:26:27 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2020-06-11 10:25:02 +0300
commit8483129b6a4746247fd7fb3b5554e10181ad937a (patch)
tree379590c11696dea9aeb1532886ddead944b65d37
parent70314b3303f712a0277b80b23bc3613744e6178e (diff)
Add workaround for MinGW GCC bug so to_stream(timestamp) properly handles %e specifier
-rw-r--r--libbutl/timestamp.cxx67
-rw-r--r--libbutl/timestamp.mxx2
-rw-r--r--tests/timestamp/driver.cxx3
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 <time.h> 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");