suyu/src/common/time_zone.cpp
lat9nq 90d76333da 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.
2023-07-10 17:51:34 -04:00

92 lines
3.4 KiB
C++

// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <exception>
#include <iomanip>
#include <sstream>
#include <stdexcept>
#include <fmt/chrono.h>
#include <fmt/core.h>
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/time_zone.h"
namespace Common::TimeZone {
// Time zone strings
constexpr std::array timezones{
"GMT", "GMT", "CET", "CST6CDT", "Cuba", "EET", "Egypt", "Eire",
"EST", "EST5EDT", "GB", "GB-Eire", "GMT", "GMT+0", "GMT-0", "GMT0",
"Greenwich", "Hongkong", "HST", "Iceland", "Iran", "Israel", "Jamaica", "Japan",
"Kwajalein", "Libya", "MET", "MST", "MST7MDT", "Navajo", "NZ", "NZ-CHAT",
"Poland", "Portugal", "PRC", "PST8PDT", "ROC", "ROK", "Singapore", "Turkey",
"UCT", "Universal", "UTC", "W-SU", "WET", "Zulu",
};
const std::array<const char*, 46>& GetTimeZoneStrings() {
return timezones;
}
std::string GetDefaultTimeZone() {
return "GMT";
}
// 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 std::time_t t{std::time(nullptr)};
const std::tm local{*std::localtime(&t)};
const std::tm gmt{*std::gmtime(&t)};
// 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_offset};
}
// Key is [Hours * 100 + Minutes], multiplied by 100 if DST
const static std::map<s64, const char*> off_timezones = {
{530, "Asia/Calcutta"}, {930, "Australia/Darwin"}, {845, "Australia/Eucla"},
{103000, "Australia/Adelaide"}, {1030, "Australia/Lord_Howe"}, {630, "Indian/Cocos"},
{1245, "Pacific/Chatham"}, {134500, "Pacific/Chatham"}, {-330, "Canada/Newfoundland"},
{-23000, "Canada/Newfoundland"}, {430, "Asia/Kabul"}, {330, "Asia/Tehran"},
{43000, "Asia/Tehran"}, {545, "Asia/Kathmandu"}, {-930, "Asia/Marquesas"},
};
std::string FindSystemTimeZone() {
const s64 seconds = static_cast<s64>(GetCurrentOffsetSeconds().count());
const s64 minutes = seconds / 60;
const s64 hours = minutes / 60;
const s64 minutes_off = minutes - hours * 60;
if (minutes_off != 0) {
const auto the_time = std::time(nullptr);
const struct std::tm& local = *std::localtime(&the_time);
const bool is_dst = local.tm_isdst != 0;
const s64 tz_index = (hours * 100 + minutes_off) * (is_dst ? 100 : 1);
try {
return off_timezones.at(tz_index);
} catch (std::out_of_range&) {
LOG_ERROR(Common, "Time zone {} not handled, defaulting to hour offset.", tz_index);
}
}
return fmt::format("Etc/GMT{:s}{:d}", hours > 0 ? "-" : "+", std::abs(hours));
}
} // namespace Common::TimeZone