diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 95f09e5d6..35c1e6d01 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -56,6 +56,7 @@ add_library(common
small_string.h
string_util.cpp
string_util.h
+ time_helpers.h
thirdparty/SmallVector.cpp
thirdparty/SmallVector.h
thirdparty/aes.cpp
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj
index 6b53194bb..004a6d0e2 100644
--- a/src/common/common.vcxproj
+++ b/src/common/common.vcxproj
@@ -23,6 +23,7 @@
+
@@ -105,6 +106,9 @@
+
+
+
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters
index 4318f4452..4933ef382 100644
--- a/src/common/common.vcxproj.filters
+++ b/src/common/common.vcxproj.filters
@@ -57,6 +57,7 @@
thirdparty
+
@@ -112,4 +113,7 @@
thirdparty
+
+
+
\ No newline at end of file
diff --git a/src/common/time_helpers.h b/src/common/time_helpers.h
new file mode 100644
index 000000000..025d91976
--- /dev/null
+++ b/src/common/time_helpers.h
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin
+// SPDX-License-Identifier: CC-BY-NC-ND-4.0
+
+#pragma once
+
+#include
+
+namespace Common {
+
+inline std::tm LocalTime(std::time_t tvalue)
+{
+ std::tm ttime;
+#ifdef _MSC_VER
+ localtime_s(&ttime, &tvalue);
+#else
+ localtime_r(&tvalue, &ttime);
+#endif
+ return ttime;
+}
+
+} // namespace Common
diff --git a/src/core/game_list.cpp b/src/core/game_list.cpp
index 8a4dbe465..48900e80a 100644
--- a/src/core/game_list.cpp
+++ b/src/core/game_list.cpp
@@ -27,6 +27,7 @@
#include "common/progress_callback.h"
#include "common/string_util.h"
#include "common/thirdparty/SmallVector.h"
+#include "common/time_helpers.h"
#include "common/timer.h"
#include "fmt/format.h"
@@ -1592,17 +1593,8 @@ std::string GameList::FormatTimestamp(std::time_t timestamp)
}
else
{
- struct tm ctime = {};
- struct tm ttime = {};
- const std::time_t ctimestamp = std::time(nullptr);
-#ifdef _MSC_VER
- localtime_s(&ctime, &ctimestamp);
- localtime_s(&ttime, ×tamp);
-#else
- localtime_r(&ctimestamp, &ctime);
- localtime_r(×tamp, &ttime);
-#endif
-
+ 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)
{
ret = TRANSLATE_STR("GameList", "Today");
diff --git a/src/core/imgui_overlays.cpp b/src/core/imgui_overlays.cpp
index d08fbd4fe..d58b6a46d 100644
--- a/src/core/imgui_overlays.cpp
+++ b/src/core/imgui_overlays.cpp
@@ -34,6 +34,7 @@
#include "common/path.h"
#include "common/string_util.h"
#include "common/thirdparty/SmallVector.h"
+#include "common/time_helpers.h"
#include "common/timer.h"
#include "IconsEmoji.h"
@@ -44,7 +45,6 @@
#include
#include
-#include
#include
#include
#include
@@ -1157,7 +1157,8 @@ void SaveStateSelectorUI::InitializeListEntry(ListEntry* li, ExtendedSaveStateIn
if (global)
li->game_details = fmt::format(TRANSLATE_FS("SaveStateSelectorUI", "{} ({})"), ssi->title, ssi->serial);
- li->summary = fmt::format(TRANSLATE_FS("SaveStateSelectorUI", DATE_TIME_FORMAT), fmt::localtime(ssi->timestamp));
+ li->summary = fmt::format(TRANSLATE_FS("SaveStateSelectorUI", DATE_TIME_FORMAT),
+ Common::LocalTime(static_cast(ssi->timestamp)));
li->filename = Path::GetFileName(path);
li->slot = slot;
li->global = global;
@@ -1440,7 +1441,7 @@ 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), fmt::localtime(sd.ModificationTime));
+ date = fmt::format(TRANSLATE_FS("SaveStateSelectorUI", DATE_TIME_FORMAT), Common::LocalTime(sd.ModificationTime));
else
date = TRANSLATE_STR("SaveStateSelectorUI", "no save yet");
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 8cd7d43bb..c6858bfa8 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -67,6 +67,7 @@
#include "common/ryml_helpers.h"
#include "common/string_util.h"
#include "common/task_queue.h"
+#include "common/time_helpers.h"
#include "common/timer.h"
#include "IconsEmoji.h"
@@ -80,6 +81,7 @@
#include "xxhash.h"
#include
+#include
#include
#include
#include
@@ -351,7 +353,7 @@ static StateVars s_state;
static TinyString GetTimestampStringForFileName()
{
- return TinyString::from_format("{:%Y-%m-%d-%H-%M-%S}", fmt::localtime(std::time(nullptr)));
+ return TinyString::from_format("{:%Y-%m-%d-%H-%M-%S}", Common::LocalTime(std::time(nullptr)));
}
bool System::PerformEarlyHardwareChecks(Error* error)
diff --git a/src/duckstation-mini/mini_host.cpp b/src/duckstation-mini/mini_host.cpp
index 400081188..f7ec22ec7 100644
--- a/src/duckstation-mini/mini_host.cpp
+++ b/src/duckstation-mini/mini_host.cpp
@@ -37,6 +37,7 @@
#include "common/path.h"
#include "common/string_util.h"
#include "common/threading.h"
+#include "common/time_helpers.h"
#include "IconsEmoji.h"
#include "fmt/format.h"
@@ -1415,15 +1416,8 @@ std::string Host::FormatNumber(NumberFormatType type, s64 value)
DefaultCaseIsUnreachable();
}
- struct tm ttime = {};
- const std::time_t tvalue = static_cast(value);
-#ifdef _MSC_VER
- localtime_s(&ttime, &tvalue);
-#else
- localtime_r(&tvalue, &ttime);
-#endif
-
char buf[128];
+ const std::tm ttime = Common::LocalTime(static_cast(value));
std::strftime(buf, std::size(buf), format, &ttime);
ret.assign(buf);
}
diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp
index 314c29107..a1f6aeb73 100644
--- a/src/duckstation-regtest/regtest_host.cpp
+++ b/src/duckstation-regtest/regtest_host.cpp
@@ -34,6 +34,7 @@
#include "common/sha256_digest.h"
#include "common/string_util.h"
#include "common/threading.h"
+#include "common/time_helpers.h"
#include "common/timer.h"
#include "fmt/format.h"
@@ -604,15 +605,8 @@ std::string Host::FormatNumber(NumberFormatType type, s64 value)
DefaultCaseIsUnreachable();
}
- struct tm ttime = {};
- const std::time_t tvalue = static_cast(value);
-#ifdef _MSC_VER
- localtime_s(&ttime, &tvalue);
-#else
- localtime_r(&tvalue, &ttime);
-#endif
-
char buf[128];
+ const std::tm ttime = Common::LocalTime(static_cast(value));
std::strftime(buf, std::size(buf), format, &ttime);
ret.assign(buf);
}
diff --git a/src/util/iso_reader.cpp b/src/util/iso_reader.cpp
index c33db3b26..8750bdd1f 100644
--- a/src/util/iso_reader.cpp
+++ b/src/util/iso_reader.cpp
@@ -10,6 +10,7 @@
#include "common/file_system.h"
#include "common/progress_callback.h"
#include "common/string_util.h"
+#include "common/time_helpers.h"
#include "fmt/format.h"
@@ -572,14 +573,8 @@ std::string IsoReader::ISODirectoryEntryDateTime::GetFormattedTime() const
const s32 uts_offset = static_cast(gmt_offset) * 3600;
const time_t uts = std::mktime(&utime) + uts_offset;
- struct tm ltime;
-#ifdef _MSC_VER
- localtime_s(<ime, &uts);
-#else
- localtime_r(&uts, <ime);
-#endif
-
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);
}