From 21c83440b3b16720abdb5c364a69e7f8e0f9051e Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 4 Oct 2025 13:12:28 +1000 Subject: [PATCH] Misc: Add error checking to LocalTime() --- src/common/time_helpers.h | 14 +++++++++----- src/core/game_list.cpp | 11 ++++++----- src/core/imgui_overlays.cpp | 9 +++++++-- src/core/system.cpp | 2 +- src/duckstation-mini/mini_host.cpp | 10 ++++++---- src/duckstation-regtest/regtest_host.cpp | 10 ++++++---- src/util/iso_reader.cpp | 13 +++++++++---- 7 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/common/time_helpers.h b/src/common/time_helpers.h index 025d91976..639586fe7 100644 --- a/src/common/time_helpers.h +++ b/src/common/time_helpers.h @@ -4,18 +4,22 @@ #pragma once #include +#include namespace Common { -inline std::tm LocalTime(std::time_t tvalue) +inline std::optional LocalTime(std::time_t tvalue) { - std::tm ttime; + std::optional ret; + ret.emplace(); #ifdef _MSC_VER - localtime_s(&ttime, &tvalue); + if (localtime_s(&ret.value(), &tvalue) != 0) + ret.reset(); #else - localtime_r(&tvalue, &ttime); + if (!localtime_r(&tvalue, &ret.value())) + ret.reset(); #endif - return ttime; + return ret; } } // namespace Common diff --git a/src/core/game_list.cpp b/src/core/game_list.cpp index 406b2b68b..c9a18fb4a 100644 --- a/src/core/game_list.cpp +++ b/src/core/game_list.cpp @@ -1594,14 +1594,15 @@ std::string GameList::FormatTimestamp(std::time_t timestamp) } else { - const std::tm ctime = Common::LocalTime(std::time(nullptr)); - const std::tm ttime = Common::LocalTime(timestamp); - if (ctime.tm_year == ttime.tm_year && ctime.tm_yday == ttime.tm_yday) + const std::optional ctime = Common::LocalTime(std::time(nullptr)); + const std::optional ttime = Common::LocalTime(timestamp); + if (ctime.has_value() && ttime.has_value() && ctime->tm_year == ttime->tm_year && ctime->tm_yday == ttime->tm_yday) { ret = TRANSLATE_STR("GameList", "Today"); } - else if ((ctime.tm_year == ttime.tm_year && ctime.tm_yday == (ttime.tm_yday + 1)) || - (ctime.tm_yday == 0 && (ctime.tm_year - 1) == ttime.tm_year)) + else if (ctime.has_value() && ttime.has_value() && + ((ctime->tm_year == ttime->tm_year && ctime->tm_yday == (ttime->tm_yday + 1)) || + (ctime->tm_yday == 0 && (ctime->tm_year - 1) == ttime->tm_year))) { ret = TRANSLATE_STR("GameList", "Yesterday"); } diff --git a/src/core/imgui_overlays.cpp b/src/core/imgui_overlays.cpp index c096d7bf5..7085f2f7f 100644 --- a/src/core/imgui_overlays.cpp +++ b/src/core/imgui_overlays.cpp @@ -1158,7 +1158,7 @@ void SaveStateSelectorUI::InitializeListEntry(ListEntry* li, ExtendedSaveStateIn li->game_details = fmt::format(TRANSLATE_FS("SaveStateSelectorUI", "{} ({})"), ssi->title, ssi->serial); li->summary = fmt::format(TRANSLATE_FS("SaveStateSelectorUI", DATE_TIME_FORMAT), - Common::LocalTime(static_cast(ssi->timestamp))); + Common::LocalTime(static_cast(ssi->timestamp)).value_or(std::tm{})); li->filename = Path::GetFileName(path); li->slot = slot; li->global = global; @@ -1458,9 +1458,14 @@ void SaveStateSelectorUI::ShowSlotOSDMessage() FILESYSTEM_STAT_DATA sd; std::string date; if (!path.empty() && FileSystem::StatFile(path.c_str(), &sd)) - date = fmt::format(TRANSLATE_FS("SaveStateSelectorUI", DATE_TIME_FORMAT), Common::LocalTime(sd.ModificationTime)); + { + date = fmt::format(TRANSLATE_FS("SaveStateSelectorUI", DATE_TIME_FORMAT), + Common::LocalTime(sd.ModificationTime).value_or(std::tm{})); + } else + { date = TRANSLATE_STR("SaveStateSelectorUI", "no save yet"); + } Host::AddIconOSDMessage( "ShowSlotOSDMessage", ICON_EMOJI_MAGNIFIYING_GLASS_TILTED_LEFT, diff --git a/src/core/system.cpp b/src/core/system.cpp index 7c0406a7b..df91e778c 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -353,7 +353,7 @@ static StateVars s_state; static TinyString GetTimestampStringForFileName() { - return TinyString::from_format("{:%Y-%m-%d-%H-%M-%S}", Common::LocalTime(std::time(nullptr))); + return TinyString::from_format("{:%Y-%m-%d-%H-%M-%S}", Common::LocalTime(std::time(nullptr)).value_or(std::tm{})); } bool System::PerformEarlyHardwareChecks(Error* error) diff --git a/src/duckstation-mini/mini_host.cpp b/src/duckstation-mini/mini_host.cpp index f7ec22ec7..24b1a17e1 100644 --- a/src/duckstation-mini/mini_host.cpp +++ b/src/duckstation-mini/mini_host.cpp @@ -1416,10 +1416,12 @@ std::string Host::FormatNumber(NumberFormatType type, s64 value) DefaultCaseIsUnreachable(); } - char buf[128]; - const std::tm ttime = Common::LocalTime(static_cast(value)); - std::strftime(buf, std::size(buf), format, &ttime); - ret.assign(buf); + ret.resize(128); + + if (const std::optional ltime = Common::LocalTime(static_cast(value))) + ret.resize(std::strftime(ret.data(), ret.size(), format, <ime.value())); + else + ret = "Invalid"; } else { diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index a1f6aeb73..b695f264d 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -605,10 +605,12 @@ std::string Host::FormatNumber(NumberFormatType type, s64 value) DefaultCaseIsUnreachable(); } - char buf[128]; - const std::tm ttime = Common::LocalTime(static_cast(value)); - std::strftime(buf, std::size(buf), format, &ttime); - ret.assign(buf); + ret.resize(128); + + if (const std::optional ltime = Common::LocalTime(static_cast(value))) + ret.resize(std::strftime(ret.data(), ret.size(), format, <ime.value())); + else + ret = "Invalid"; } else { diff --git a/src/util/iso_reader.cpp b/src/util/iso_reader.cpp index 8750bdd1f..289c16d41 100644 --- a/src/util/iso_reader.cpp +++ b/src/util/iso_reader.cpp @@ -573,8 +573,13 @@ std::string IsoReader::ISODirectoryEntryDateTime::GetFormattedTime() const const s32 uts_offset = static_cast(gmt_offset) * 3600; const time_t uts = std::mktime(&utime) + uts_offset; - char buf[128]; - const std::tm ltime = Common::LocalTime(uts); - const size_t len = std::strftime(buf, std::size(buf), "%c", <ime); - return std::string(buf, len); + std::string ret; + ret.resize(128); + + if (const std::optional ltime = Common::LocalTime(uts)) + ret.resize(std::strftime(ret.data(), ret.size(), "%c", <ime.value())); + else + ret = "Invalid"; + + return ret; }