From 90d76333da843f7058ff527bae0bdceaa1d3cd69 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 d8d7896c6..a25f8b040 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 833306bf5eeeb65d1b1fb68a17d84b077069b33d 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 5972480e5..d4e55f988 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 13755c09036aefe9209360bf0a94c3e2a83e12b8 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 a25f8b040..1ee63bf80 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 9e0d6f7d546fba694d0f87864d508faeca64c3e1 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 1ee63bf80..6131957ef 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 63a0a1f826e7d7582025fb1550b2c4034bd45b48 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 6131957ef..69e728a9d 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 {