perf(chat): use reentrant local time for chat timestamps

The open chat thread called std::localtime at least twice per message
per frame (two day keys for grouping), and more for day separators and
time labels. On glibc every call re-checks the timezone with a stat of
/etc/localtime. The chat time helpers now use util::localTime() from
the new util/local_time.h: localtime_r (localtime_s on Windows), with
no per-call timezone check and no shared static result buffer.

glibc's localtime_r reads the timezone only once, so local_time.h also
adds util::refreshTimezone() (tzset) for caches to call when they
rebuild. Nothing calls it yet; its first caller is the chat thread's
grouping-flags cache in the next commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 09:50:03 -05:00
co-authored by Claude Opus 5.5
parent e45f4c7d90
commit a69380817e
4 changed files with 62 additions and 21 deletions
+22
View File
@@ -64,6 +64,7 @@
#include "data/seed_migration_resume.h"
#include "util/address_validation.h"
#include "util/seed_phrase.h"
#include "util/local_time.h"
#include "util/image_decode_core.h"
#include "daemon/daemon_startup_diagnosis.h"
#include "util/amount_format.h"
@@ -14408,6 +14409,26 @@ void testConsolidationRunSimulation()
EXPECT_TRUE(runVerdict(sum) == RunVerdict::Clean);
}
namespace {
void testLocalTime()
{
#ifndef _WIN32
const char* prev = std::getenv("TZ");
const bool hadTz = prev != nullptr;
const std::string savedTz = hadTz ? prev : "";
setenv("TZ", "XXX-3", 1); dragonx::util::refreshTimezone(); // POSIX TZ: UTC+3, no tzdata needed
std::tm t{};
EXPECT_TRUE(dragonx::util::localTime(0, t));
EXPECT_EQ(t.tm_year, 70); EXPECT_EQ(t.tm_mday, 1); EXPECT_EQ(t.tm_hour, 3);
setenv("TZ", "YYY5", 1); dragonx::util::refreshTimezone(); // UTC-5: refreshTimezone() must pick it up
EXPECT_TRUE(dragonx::util::localTime(0, t));
EXPECT_EQ(t.tm_mday, 31); EXPECT_EQ(t.tm_hour, 19);
if (hadTz) setenv("TZ", savedTz.c_str(), 1); else unsetenv("TZ");
dragonx::util::refreshTimezone();
#endif
}
} // namespace
int main()
{
testConnectionConfig();
@@ -14644,6 +14665,7 @@ int main()
testImageDecodeCore();
testImageDecodeJobs();
testGeneratedResourceBehavior();
testLocalTime();
if (g_failures != 0) {
std::cerr << g_failures << " test assertion(s) failed\n";