From efeb79f380934b58b8d0f27167c5682e77efb41f Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Mon, 10 Jul 2023 17:51:34 -0400 Subject: [PATCH 1/5] time_zone: Remove string ops for determing zone MinGW's strftime implementation does not work and cannot be used to determine the time zone. Besides that, the string operations are actually unnecessary since we can get the offset from std::localtime. Compare localtime to gmtime to find the zone offset on all platforms. --- src/common/time_zone.cpp | 43 +++++++++++++++------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp index d8d7896c61..a25f8b040d 100644 --- a/src/common/time_zone.cpp +++ b/src/common/time_zone.cpp @@ -33,32 +33,27 @@ std::string GetDefaultTimeZone() { return "GMT"; } -static std::string GetOsTimeZoneOffset() { - const std::time_t t{std::time(nullptr)}; - const std::tm tm{*std::localtime(&t)}; - - return fmt::format("{:%z}", tm); -} - -static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) { - try { - return std::stoi(timezone); - } catch (const std::invalid_argument&) { - LOG_CRITICAL(Common, "invalid_argument with {}!", timezone); - return 0; - } catch (const std::out_of_range&) { - LOG_CRITICAL(Common, "out_of_range with {}!", timezone); - return 0; - } +// Results are not comparable to seconds since Epoch +static std::time_t TmSpecToSeconds(const struct std::tm& spec) { + std::time_t cumulative = spec.tm_year; + cumulative = cumulative * 365 + spec.tm_yday; // Years to days + cumulative = cumulative * 24 + spec.tm_hour; // Days to hours + cumulative = cumulative * 60 + spec.tm_min; // Hours to minutes + cumulative = cumulative * 60 + spec.tm_sec; // Minutes to seconds + return cumulative; } std::chrono::seconds GetCurrentOffsetSeconds() { - const int offset{ConvertOsTimeZoneOffsetToInt(GetOsTimeZoneOffset())}; + const std::time_t t{std::time(nullptr)}; + const std::tm local{*std::localtime(&t)}; + const std::tm gmt{*std::gmtime(&t)}; - int seconds{(offset / 100) * 60 * 60}; // Convert hour component to seconds - seconds += (offset % 100) * 60; // Convert minute component to seconds + // gmt_seconds is a different offset than time(nullptr) + const auto gmt_seconds = TmSpecToSeconds(gmt); + const auto local_seconds = TmSpecToSeconds(local); + const auto seconds_offset = gmt_seconds - local_seconds; - return std::chrono::seconds{seconds}; + return std::chrono::seconds{seconds_offset}; } // Key is [Hours * 100 + Minutes], multiplied by 100 if DST @@ -71,11 +66,6 @@ const static std::map off_timezones = { }; std::string FindSystemTimeZone() { -#if defined(MINGW) - // MinGW has broken strftime -- https://sourceforge.net/p/mingw-w64/bugs/793/ - // e.g. fmt::format("{:%z}") -- returns "Eastern Daylight Time" when it should be "-0400" - return timezones[0]; -#else const s64 seconds = static_cast(GetCurrentOffsetSeconds().count()); const s64 minutes = seconds / 60; @@ -97,7 +87,6 @@ std::string FindSystemTimeZone() { } } return fmt::format("Etc/GMT{:s}{:d}", hours > 0 ? "-" : "+", std::abs(hours)); -#endif } } // namespace Common::TimeZone From 53d03b9a0ef86b1532002c1f03ff3ef17f4a0a71 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Mon, 10 Jul 2023 17:52:35 -0400 Subject: [PATCH 2/5] settings: Disable C++20 tzdb path on MinGW This path always results in Etc/UTC on MinGW, which often is not close to the local time zone. --- src/common/settings.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 5972480e51..d4e55f9888 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -26,7 +26,8 @@ std::string GetTimeZoneString() { std::string location_name; if (time_zone_index == 0) { // Auto -#if __cpp_lib_chrono >= 201907L +#if __cpp_lib_chrono >= 201907L && !defined(MINGW) + // Disabled for MinGW -- tzdb always returns Etc/UTC try { const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb(); const std::chrono::time_zone* current_zone = time_zone_data.current_zone(); From 9fde7a84ea395660c29b510f4e8cb4ff9d92922f Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 12 Jul 2023 02:34:02 -0400 Subject: [PATCH 3/5] time_zone: Account for leap years Protects against invalid hour offsets during transitions to years following leap years. --- src/common/time_zone.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp index a25f8b040d..1ee63bf802 100644 --- a/src/common/time_zone.cpp +++ b/src/common/time_zone.cpp @@ -35,11 +35,13 @@ std::string GetDefaultTimeZone() { // Results are not comparable to seconds since Epoch static std::time_t TmSpecToSeconds(const struct std::tm& spec) { + const int year = spec.tm_year - 1; // Years up to now + const int leap_years = year / 4 - year / 100; std::time_t cumulative = spec.tm_year; - cumulative = cumulative * 365 + spec.tm_yday; // Years to days - cumulative = cumulative * 24 + spec.tm_hour; // Days to hours - cumulative = cumulative * 60 + spec.tm_min; // Hours to minutes - cumulative = cumulative * 60 + spec.tm_sec; // Minutes to seconds + cumulative = cumulative * 365 + leap_years + spec.tm_yday; // Years to days + cumulative = cumulative * 24 + spec.tm_hour; // Days to hours + cumulative = cumulative * 60 + spec.tm_min; // Hours to minutes + cumulative = cumulative * 60 + spec.tm_sec; // Minutes to seconds return cumulative; } From 0331d580e21f5f4b949254eaa174799abdd6c01a Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 12 Jul 2023 03:02:45 -0400 Subject: [PATCH 4/5] time_zone: Swap subtraction order --- src/common/time_zone.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp index 1ee63bf802..6131957efd 100644 --- a/src/common/time_zone.cpp +++ b/src/common/time_zone.cpp @@ -53,7 +53,7 @@ std::chrono::seconds GetCurrentOffsetSeconds() { // gmt_seconds is a different offset than time(nullptr) const auto gmt_seconds = TmSpecToSeconds(gmt); const auto local_seconds = TmSpecToSeconds(local); - const auto seconds_offset = gmt_seconds - local_seconds; + const auto seconds_offset = local_seconds - gmt_seconds; return std::chrono::seconds{seconds_offset}; } From 25b800e26c71dc1fe051bc0c016fec096ccf4648 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 12 Jul 2023 03:03:03 -0400 Subject: [PATCH 5/5] time_zone: Clean up includes --- src/common/time_zone.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp index 6131957efd..69e728a9df 100644 --- a/src/common/time_zone.cpp +++ b/src/common/time_zone.cpp @@ -4,13 +4,13 @@ #include #include #include +#include #include #include #include #include #include "common/logging/log.h" -#include "common/settings.h" #include "common/time_zone.h" namespace Common::TimeZone {