// file : tests/timestamp/driver.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #include <time.h> // tzset() (POSIX), _tzset() (Windows) #include <chrono> #include <locale> #include <clocale> #include <sstream> #include <iomanip> #include <system_error> #include <libbutl/timestamp.hxx> #undef NDEBUG #include <cassert> using namespace std; using namespace butl; // Parse the input using the format string. Print the resulting time with the // same format string, ensure the output matches the input. // static bool parse (const char* in, const char* fmt, bool local, string out) { if (out.empty ()) out = in; try { const char* e; timestamp t (from_string (in, fmt, local, &e)); ostringstream o; if (!to_stream (o, t, fmt, false, local)) return false; return o.str () + e == out; } catch (...) { return false; } } static bool parse (const char* in, const char* fmt, const string& out = "") { return parse (in, fmt, true, out) && parse (in, fmt, false, out); } static bool fail (const char* in, const char* fmt) { try { from_string (in, fmt, true); return false; } catch (const system_error&) { return true; } } // Convert nanoseconds to string according to the butl::duration period. // static string ns (unsigned long long t) { duration d (chrono::duration_cast<duration> (chrono::nanoseconds (t))); chrono::nanoseconds n (chrono::duration_cast<chrono::nanoseconds> (d)); ostringstream o; o.fill ('0'); o << setw (9) << n.count (); return o.str (); } int main () { // To use butl::to_stream() later on. // #ifndef _WIN32 tzset (); #else _tzset (); #endif // Invalid %[]. // assert (fail ("Apr 08 19:31:10 2016", "%b %d %H:%M:%S%[")); assert (fail ("Apr 08 19:31:10 2016", "%b %d %H:%M:%S%[.")); assert (fail ("Apr 08 19:31:10 2016", "%b %d %H:%M:%S%[.U")); assert (fail ("Apr 08 19:31:10 2016", "%b %d %H:%M:%S%[.A]")); assert (fail ("Apr 08 19:31:10 2016", "%d %H:%M:%S%[.U] %Y")); assert (fail ("2016-10-20 11:12:13.123456789", "%Y-%m-%d %H:%M:%S%[N]")); // Invalid fraction of a second. // assert (fail ("Apr 08 19:31:10. 2016", "%b %d %H:%M:%S%[.U] %Y")); assert (fail ("Apr 08 19:31:10.1 2016", "%b %d %H:%M:%S%[.M] %Y")); assert (fail ("Apr 08 19:31:10.12 2016", "%b %d %H:%M:%S%[.M] %Y")); assert (fail ("Apr 08 19:31:10.", "%b %d %H:%M:%S%[.U] %Y")); assert (fail ("Apr 08 19:31:10.1", "%b %d %H:%M:%S%[.M] %Y")); assert (fail ("Apr 08 19:31:10.12", "%b %d %H:%M:%S%[.M] %Y")); // Input is not fully parsed. // assert (fail ( "Feb 21 19:31:10.123456789 2016 GMT", "%b %d %H:%M:%S%[.N] %Y")); // Invalid input (%[] unrelated). // assert (fail ("Apr 08 19:31:10.123456789 ABC", "%b %d %H:%M:%S%[.N] %Y")); // This doesn't work in VC16 because their implementation of std::get_time() // has a bug. Due to this bug std::get_time() parses the input // "Apr 19:31:10 2016" for the format "%b %d %H:%M:%S %Y" as if the input were // "Apr 19 00:31:10 2016". // #if !defined(_MSC_VER) || _MSC_VER >= 2000 assert (fail ("Apr 19:31:10 2016", "%b %d %H:%M:%S %Y")); assert (fail (":31 2016", "%H:%M %Y")); #endif assert (fail ("Opr 08 19:31:10 2016", "%b %d %H:%M:%S %Y")); // Parse valid input with a valid format. // assert (parse ( "Apr 18 19:31:10 2016", "%b %d %H:%M:%S %Y", "Apr 18 19:31:10 2016")); assert (parse ("Apr 08 19:31:10 2016", "%b %d %H:%M:%S %Y")); assert (parse ("2016-04-08 19:31:10", "%Y-%m-%d %H:%M:%S")); assert (parse ("ABC=Apr 18 19:31:10 2016 ABC", "ABC=%b %d %H:%M:%S %Y")); assert (parse ("ABC=2016-04-08 19:31:10 ABC", "ABC=%Y-%m-%d %H:%M:%S")); assert (parse ("Feb 11 19:31:10 2016 GMT", "%b %d %H:%M:%S%[.N] %Y")); assert (parse ("2016-02-11 19:31:10 GMT", "%Y-%m-%d %H:%M:%S%[.N]")); assert (parse ("Feb 21 19:31:10.384902285 2016 GMT", "%b %d %H:%M:%S%[.N] %Y", "Feb 21 19:31:10." + ns (384902285) + " 2016 GMT")); assert (parse ("2016-02-21 19:31:10.384902285 GMT", "%Y-%m-%d %H:%M:%S%[.N]", "2016-02-21 19:31:10." + ns (384902285) + " GMT")); assert (parse ("Feb 21 19:31:10 .384902285 2016 GMT", "%b %d %H:%M:%S %[.N] %Y", "Feb 21 19:31:10 ." + ns (384902285) + " 2016 GMT")); assert (parse ("2016-02-21 19:31:10 .384902285 GMT", "%Y-%m-%d %H:%M:%S %[.N]", "2016-02-21 19:31:10 ." + ns (384902285) + " GMT")); assert (parse ( "2016-02-21 19:31:10 .384902285 GMT", "%Y-%m-%d %H:%M:%S %[.N]", "2016-02-21 19:31:10 ." + ns (384902285) + " GMT")); assert (parse ( "2016-02-21 19:31:10 .384902285 GMT", "%Y-%m-%d %H:%M:%S %[.N]", "2016-02-21 19:31:10 ." + ns (384902285) + " GMT")); assert (parse ("Feb 21 19:31:10 .384902285NS 2016 GMT", "%b %d %H:%M:%S %[.N]NS %Y", "Feb 21 19:31:10 ." + ns (384902285) + "NS 2016 GMT")); assert (parse ("2016-02-21 19:31:10 .384902285NS GMT", "%Y-%m-%d %H:%M:%S %[.N]NS", "2016-02-21 19:31:10 ." + ns (384902285) + "NS GMT")); assert (parse (".384902285 Feb 21 19:31:10 2016", "%[.N] %b %d %H:%M:%S %Y", '.' + ns (384902285) + " Feb 21 19:31:10 2016")); assert (parse (".384902285 2016-02-21 19:31:10", "%[.N] %Y-%m-%d %H:%M:%S", '.' + ns (384902285) + " 2016-02-21 19:31:10")); assert (parse (".3849022852016-02-21 19:31:10", "%[.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"); // setlocale (LC_ALL, "German_Germany.1252"); // locale::global (locale ("")); // locale::global (locale ("de_DE.utf-8")); /* assert (parse ("Mai 11 19:31:10 2016 GMT", "%b %d %H:%M:%S%[.N] %Y")); locale::global (locale ("C")); */ // @@ When debuging strptime() fallback implementation compiled with GCC // 5.3.1, the following asserts will fail due to bugs in implementation // of std::get_time() manipulator. So need to be commented out. // assert (fail ("Apr 08 19:31:10 2016", "%b %d %H:%M:%S %Y %")); // Error is not detected on FreeBSD 11 with Clang/libc++ 3.8.0 and with // Apple Clang 9.1.0 (strptime() seems to be broken). // #if !(defined(__FreeBSD__) || defined(__APPLE__)) assert (fail ("Apr 08 19:31:10", "%b %d %H:%M:%S %Y")); #endif assert (parse ( "Apr 8 19:31:10 2016", "%b %d %H:%M:%S %Y", "Apr 08 19:31:10 2016")); { timestamp t (from_string ("Apr 8 19:31:10 2016", "%b %d %H:%M:%S %Y", true /* local */)); timestamp mt (from_string ("Apr 8 00:00:00 2016", "%b %d %H:%M:%S %Y", true /* local */)); assert (daytime (t) == t - mt); } }