Compare commits

...

13 Commits

Author SHA1 Message Date
lat9nq
4164a674f0 time_zone_manager: Implement go_ahead/go_back 2023-05-26 04:41:20 -04:00
lat9nq
e8b6d23522 time_zone: Don't double up the std::abs
Too many absolute values were causing mirrored time zones to resolve
as the same.
2023-05-26 00:39:12 -04:00
lat9nq
3d32ff15cd time_zone_manager: Don't loop on zero
Avoid an infinite loop, trying to accumulate a value with zero.
2023-05-25 23:44:34 -04:00
lat9nq
c90126ceba common: Various fixes 2023-05-23 21:28:51 -04:00
lat9nq
4f93db57cc time_zone_binary: Add zoneinfo data
Adds the basic time zone data for the system archive.
2023-05-23 18:34:20 -04:00
lat9nq
dbbb4b0674 tz_content_manager: Try the system time zone first
If we can't find the normal time zone string, try searching for the
closest one.
2023-05-23 18:33:34 -04:00
lat9nq
20f14b50fd common: Move system time zone string detection
Moves it from Settings to Common::TimeZone, since this algorithm doesn't
depend on the setting. It also lets us use it in other libraries.
2023-05-23 18:32:28 -04:00
lat9nq
6d7dcdc874 GetTimeZoneString: Use standard features
Also forces GMT on MinGW due to broken strftime.
2023-05-22 14:19:08 -04:00
lat9nq
58d0337de8 configure_system: Remove external offset on custom rtc 2023-05-21 16:29:11 -04:00
lat9nq
35070e85f9 time: Remove auto timezone consideration
GetTimeZoneString no longer reports a setting unique to yuzu, so we
can assume a valid timezone string in core.
2023-05-21 16:29:11 -04:00
lat9nq
212b42f8db settings: Always report a valid time zone
Prevents needing to deduce the non-Switch setting in core. Instead, we
deduce the meaning of this setting where the heresy is committed, in
common.

settings: Remove strftime usage
2023-05-21 16:29:10 -04:00
lat9nq
4d04d30087 time_manager: Don't offset RTC by system time zone
This causes the emulated system's universal time to be on the user's clock, and the user time to
be off if they set a time zone.

time_manager: Remove GetExternalRtcTime
2023-05-21 16:29:08 -04:00
lat9nq
383c68f717 tz_content_manager: Detect system time zone
Uses C++20 tzdb to determine the system timezone. The switch uses the
597 posix time zones, so this needs tests if the system time zone isn't
posix-compliant.
2023-05-21 16:29:08 -04:00
9 changed files with 1996 additions and 88 deletions

View File

@@ -1,12 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#if __cpp_lib_chrono >= 201907L
#include <chrono>
#endif
#include <string_view>
#include "common/assert.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/time_zone.h"
namespace Settings {
@@ -14,18 +18,23 @@ Values values;
static bool configuring_global = true;
std::string GetTimeZoneString() {
static constexpr std::array timezones{
"auto", "default", "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 auto time_zone_index = static_cast<std::size_t>(values.time_zone_index.GetValue());
ASSERT(time_zone_index < timezones.size());
return timezones[time_zone_index];
ASSERT(time_zone_index < Common::TimeZone::GetTimeZoneStrings().size());
std::string location_name;
if (time_zone_index == 0) { // Auto
#if __cpp_lib_chrono >= 201907L
const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb();
const std::chrono::time_zone* current_zone = time_zone_data.current_zone();
std::string_view current_zone_name = current_zone->name();
location_name = current_zone_name;
#else
location_name = Common::TimeZone::FindSystemTimeZone();
#endif
} else {
location_name = Common::TimeZone::GetTimeZoneStrings()[time_zone_index];
}
return location_name;
}
void LogSettings() {

View File

@@ -4,12 +4,29 @@
#include <chrono>
#include <iomanip>
#include <sstream>
#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";
}
@@ -18,10 +35,7 @@ static std::string GetOsTimeZoneOffset() {
const std::time_t t{std::time(nullptr)};
const std::tm tm{*std::localtime(&t)};
std::stringstream ss;
ss << std::put_time(&tm, "%z"); // Get the current timezone offset, e.g. "-400", as a string
return ss.str();
return fmt::format("{:%z}", tm);
}
static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) {
@@ -45,4 +59,57 @@ std::chrono::seconds GetCurrentOffsetSeconds() {
return std::chrono::seconds{seconds};
}
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
// Time zone offset in seconds from GMT
constexpr std::array offsets{
0, 0, 3600, -21600, -19768, 7200, 7509, -1521, -18000, -18000, -75, -75,
0, 0, 0, 0, 0, 27402, -36000, -968, 12344, 8454, -18430, 33539,
40160, 3164, 3600, -25200, -25200, -25196, 41944, 44028, 5040, -2205, 29143, -28800,
29160, 30472, 24925, 6952, 0, 0, 0, 9017, 0, 0,
};
// If the time zone recognizes Daylight Savings Time
constexpr std::array dst{
false, false, true, true, true, true, true, true, false, true, true, true,
false, false, false, false, false, true, false, false, true, true, true, true,
false, true, true, false, true, true, true, true, true, true, true, true,
true, true, true, true, false, false, false, true, true, false,
};
static std::string system_time_zone_cached{};
if (!system_time_zone_cached.empty()) {
return system_time_zone_cached;
}
const auto now = std::time(nullptr);
const struct std::tm& local = *std::localtime(&now);
const s64 system_offset = GetCurrentOffsetSeconds().count() - (local.tm_isdst ? 3600 : 0);
int min = std::numeric_limits<int>::max();
int min_index = -1;
for (u32 i = 2; i < offsets.size(); i++) {
// Skip if system is celebrating DST but considered time zone does not
if (local.tm_isdst && !dst[i]) {
continue;
}
const auto offset = offsets[i];
const int difference = static_cast<int>(std::abs(offset - system_offset));
if (difference < min) {
min = difference;
min_index = i;
}
}
system_time_zone_cached = GetTimeZoneStrings()[min_index];
return system_time_zone_cached;
#endif
}
} // namespace Common::TimeZone

View File

@@ -3,15 +3,21 @@
#pragma once
#include <array>
#include <chrono>
#include <string>
namespace Common::TimeZone {
[[nodiscard]] const std::array<const char*, 46>& GetTimeZoneStrings();
/// Gets the default timezone, i.e. "GMT"
[[nodiscard]] std::string GetDefaultTimeZone();
/// Gets the offset of the current timezone (from the default), in seconds
[[nodiscard]] std::chrono::seconds GetCurrentOffsetSeconds();
/// Searches time zone offsets for the closest offset to the system time zone
[[nodiscard]] std::string FindSystemTimeZone();
} // namespace Common::TimeZone

File diff suppressed because it is too large Load Diff

View File

@@ -22,10 +22,6 @@ s64 GetSecondsSinceEpoch() {
return std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch).count() +
Settings::values.custom_rtc_differential;
}
s64 GetExternalRtcValue() {
return GetSecondsSinceEpoch() + TimeManager::GetExternalTimeZoneOffset();
}
} // Anonymous namespace
struct TimeManager::Impl final {
@@ -43,7 +39,7 @@ struct TimeManager::Impl final {
std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
time_zone_content_manager{system} {
const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
const auto system_time{Clock::TimeSpanType::FromSeconds(GetSecondsSinceEpoch())};
SetupStandardSteadyClock(system, Common::UUID::MakeRandom(), system_time, {}, {});
SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
@@ -123,14 +119,6 @@ struct TimeManager::Impl final {
time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
}
static s64 GetExternalTimeZoneOffset() {
// With "auto" timezone setting, we use the external system's timezone offset
if (Settings::GetTimeZoneString() == "auto") {
return Common::TimeZone::GetCurrentOffsetSeconds().count();
}
return 0;
}
void SetupStandardSteadyClock(Core::System& system_, Common::UUID clock_source_id,
Clock::TimeSpanType setup_value,
Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected) {
@@ -301,13 +289,4 @@ void TimeManager::SetupTimeZoneManager(std::string location_name,
impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point,
total_location_name_count, time_zone_rule_version, vfs_file);
}
/*static*/ s64 TimeManager::GetExternalTimeZoneOffset() {
// With "auto" timezone setting, we use the external system's timezone offset
if (Settings::GetTimeZoneString() == "auto") {
return Common::TimeZone::GetCurrentOffsetSeconds().count();
}
return 0;
}
} // namespace Service::Time

View File

@@ -64,8 +64,6 @@ public:
std::size_t total_location_name_count, u128 time_zone_rule_version,
FileSys::VirtualFile& vfs_file);
static s64 GetExternalTimeZoneOffset();
private:
Core::System& system;

View File

@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <sstream>
#include "common/logging/log.h"
@@ -12,7 +13,11 @@
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_types.h"
#include "core/hle/result.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/time/errors.h"
#include "core/hle/service/time/time_manager.h"
#include "core/hle/service/time/time_zone_content_manager.h"
@@ -71,20 +76,14 @@ TimeZoneContentManager::TimeZoneContentManager(Core::System& system_)
: system{system_}, location_name_cache{BuildLocationNameCache(system)} {}
void TimeZoneContentManager::Initialize(TimeManager& time_manager) {
std::string location_name;
const auto timezone_setting = Settings::GetTimeZoneString();
if (timezone_setting == "auto" || timezone_setting == "default") {
location_name = Common::TimeZone::GetDefaultTimeZone();
} else {
location_name = timezone_setting;
}
if (FileSys::VirtualFile vfs_file;
GetTimeZoneInfoFile(location_name, vfs_file) == ResultSuccess) {
GetTimeZoneInfoFile(timezone_setting, vfs_file) == ResultSuccess) {
const auto time_point{
time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
time_manager.SetupTimeZoneManager(location_name, time_point, location_name_cache.size(), {},
vfs_file);
time_manager.SetupTimeZoneManager(timezone_setting, time_point, location_name_cache.size(),
{}, vfs_file);
} else {
time_zone_manager.MarkAsInitialized();
}
@@ -126,8 +125,15 @@ Result TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_n
vfs_file = zoneinfo_dir->GetFileRelative(location_name);
if (!vfs_file) {
LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.",
time_zone_binary_titleid, location_name);
LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using system timezone.",
time_zone_binary_titleid, location_name);
const std::string system_time_zone{Common::TimeZone::FindSystemTimeZone()};
vfs_file = zoneinfo_dir->GetFile(system_time_zone);
}
if (!vfs_file) {
LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.",
time_zone_binary_titleid, location_name);
vfs_file = zoneinfo_dir->GetFile(Common::TimeZone::GetDefaultTimeZone());
}

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <climits>
#include <limits>
#include "common/assert.h"
#include "common/logging/log.h"
@@ -9,6 +10,7 @@
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/service/time/time_zone_manager.h"
#include "core/hle/service/time/time_zone_types.h"
namespace Service::Time::TimeZone {
@@ -142,6 +144,9 @@ static constexpr bool GetInteger(const char* name, int& offset, int& value, int
if (!IsDigit(temp)) {
return {};
}
if (temp == '0') {
return {};
}
do {
value = value * 10 + (temp - '0');
if (value > max) {
@@ -629,11 +634,47 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi
UNIMPLEMENTED();
}
}
const auto typesequiv = [](TimeZoneRule& rule, int a, int b) -> bool {
if (a < 0 || a >= rule.type_count || b < 0 || b >= rule.type_count) {
return {};
}
const struct TimeTypeInfo* ap = &rule.ttis[a];
const struct TimeTypeInfo* bp = &rule.ttis[b];
return (ap->gmt_offset == bp->gmt_offset && ap->is_dst == bp->is_dst &&
(std::strcmp(&rule.chars[ap->abbreviation_list_index],
&rule.chars[bp->abbreviation_list_index]) == 0));
};
if (time_zone_rule.type_count == 0) {
return {};
}
if (time_zone_rule.time_count > 1) {
UNIMPLEMENTED();
if (time_zone_rule.ats[0] <= std::numeric_limits<s64>::max() - seconds_per_repeat) {
s64 repeatat = time_zone_rule.ats[0] + seconds_per_repeat;
int repeatattype = time_zone_rule.types[0];
for (int i = 1; i < time_zone_rule.time_count; ++i) {
if (time_zone_rule.ats[i] == repeatat &&
typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) {
time_zone_rule.go_back = true;
break;
}
}
}
if (std::numeric_limits<s64>::min() + seconds_per_repeat <=
time_zone_rule.ats[time_zone_rule.time_count - 1]) {
s64 repeatat = time_zone_rule.ats[time_zone_rule.time_count - 1] - seconds_per_repeat;
int repeatattype = time_zone_rule.types[time_zone_rule.time_count - 1];
for (int i = time_zone_rule.time_count; i >= 0; --i) {
if (time_zone_rule.ats[i] == repeatat &&
typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) {
time_zone_rule.go_ahead = true;
break;
}
}
}
}
s32 default_type{};

View File

@@ -144,8 +144,7 @@ void ConfigureSystem::ApplyConfiguration() {
if (ui->custom_rtc_checkbox->isChecked()) {
Settings::values.custom_rtc = ui->custom_rtc_edit->dateTime().toSecsSinceEpoch();
if (system.IsPoweredOn()) {
const s64 posix_time{*Settings::values.custom_rtc +
Service::Time::TimeManager::GetExternalTimeZoneOffset()};
const s64 posix_time{*Settings::values.custom_rtc};
system.GetTimeManager().UpdateLocalSystemClockTime(posix_time);
}
} else {