diff options
author | Simon Warta <[email protected]> | 2015-07-17 17:10:02 +0200 |
---|---|---|
committer | Simon Warta <[email protected]> | 2015-07-27 13:04:40 +0200 |
commit | d4be8c6f64420bcc0f55ce6ae18bf9363bd46481 (patch) | |
tree | 18cf389fa35efd254a8a410a57b2165dca787ed7 /src/lib/utils/calendar.cpp | |
parent | 1f3ef881b103c4b8734691051f5de38f99c912af (diff) |
Add mktime fallback for non-POSIX timegm()
Closes #202
Diffstat (limited to 'src/lib/utils/calendar.cpp')
-rw-r--r-- | src/lib/utils/calendar.cpp | 61 |
1 files changed, 56 insertions, 5 deletions
diff --git a/src/lib/utils/calendar.cpp b/src/lib/utils/calendar.cpp index f62303e49..966c97730 100644 --- a/src/lib/utils/calendar.cpp +++ b/src/lib/utils/calendar.cpp @@ -11,6 +11,7 @@ #include <ctime> #include <sstream> #include <iomanip> +#include <mutex> namespace Botan { @@ -34,11 +35,53 @@ std::tm do_gmtime(std::time_t time_val) return tm; } +#if !defined(BOTAN_TARGET_OS_HAS_TIMEGM) && !defined(BOTAN_TARGET_OS_HAS_MKGMTIME) + +#pragma message "Caution! A fallback version of timegm() is used which is not thread-safe" + +std::mutex ENV_TZ; + +std::time_t fallback_timegm(std::tm *tm) + { + std::time_t out; + std::string tz_backup; + + ENV_TZ.lock(); + + // Store current value of env variable TZ + const char* tz_env_pointer = ::getenv("TZ"); + if (tz_env_pointer != nullptr) + tz_backup = std::string(tz_env_pointer); + + // Clear value of TZ + ::setenv("TZ", "", 1); + ::tzset(); + + out = ::mktime(tm); + + // Restore TZ + if (!tz_backup.empty()) + { + // setenv makes a copy of the second argument + ::setenv("TZ", tz_backup.data(), 1); + } + else + { + ::unsetenv("TZ"); + } + ::tzset(); + + ENV_TZ.unlock(); + + return out; +} +#endif + } std::chrono::system_clock::time_point calendar_point::to_std_timepoint() { - if (year < 1900) + if (year < 1970) throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years before 1990."); // 32 bit time_t ends at January 19, 2038 @@ -48,7 +91,9 @@ std::chrono::system_clock::time_point calendar_point::to_std_timepoint() if (year > 2037) throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2037."); + // std::tm: struct without any timezone information std::tm tm; + tm.tm_isdst = -1; // i.e. no DST information available tm.tm_sec = seconds; tm.tm_min = minutes; tm.tm_hour = hour; @@ -56,12 +101,18 @@ std::chrono::system_clock::time_point calendar_point::to_std_timepoint() tm.tm_mon = month - 1; tm.tm_year = year - 1900; - // Convert std::tm to std::time_t + // Define a function alias `botan_timegm` + #if defined(BOTAN_TARGET_OS_HAS_TIMEGM) + std::time_t (&botan_timegm)(std::tm *tm) = timegm; + #elif defined(BOTAN_TARGET_OS_HAS_MKGMTIME) // http://stackoverflow.com/questions/16647819/timegm-cross-platform - #if defined(BOTAN_TARGET_OS_IS_WINDOWS) - #define timegm _mkgmtime + std::time_t (&botan_timegm)(std::tm *tm) = _mkgmtime; + #else + std::time_t (&botan_timegm)(std::tm *tm) = fallback_timegm; #endif - std::time_t tt = timegm(&tm); + + // Convert std::tm to std::time_t + std::time_t tt = botan_timegm(&tm); if (tt == -1) throw Invalid_Argument("calendar_point couldn't be converted: " + to_string()); |