#cmakedefine HAVE_SYS_MMAN_H
// Define if you have the <sys/sendfile.h> header file.
-#cmakedefine HAVE_SYS_SENDFILE_H
+#cmakedefine HAVE_SYS_SENDFILE_H
// Define if you have the <sys/utime.h> header file.
#cmakedefine HAVE_SYS_UTIME_H
// Define if you have the "getpwuid" function.
#cmakedefine HAVE_GETPWUID
+// Define if you have the "gmtime_r" function.
+#cmakedefine HAVE_GMTIME_R
+
// Define if you have the "localtime_r" function.
#cmakedefine HAVE_LOCALTIME_R
// Copyright (C) 2002 Andrew Tridgell
-// Copyright (C) 2009-2024 Joel Rosdahl and other contributors
+// Copyright (C) 2009-2025 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
#include <ccache/util/filesystem.hpp>
#include <ccache/util/format.hpp>
#include <ccache/util/logging.hpp>
+#include <ccache/util/string.hpp>
#include <ccache/util/time.hpp>
#include <string>
static char prefix[200];
if (!bulk || prefix[0] == '\0') {
- char timestamp[100];
- auto now = util::TimePoint::now();
- auto tm = util::localtime(now);
- if (tm) {
- strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", &*tm);
- } else {
- snprintf(timestamp,
- sizeof(timestamp),
- "%llu",
- static_cast<long long unsigned int>(now.sec()));
- }
+ const auto now = util::TimePoint::now();
snprintf(prefix,
sizeof(prefix),
"[%s.%06u %-5d] ",
- timestamp,
+ util::format_iso8601_timestamp(now).c_str(),
static_cast<unsigned int>(now.nsec_decimal_part() / 1000),
static_cast<int>(getpid()));
}
#include <ccache/util/assertions.hpp>
#include <ccache/util/filesystem.hpp>
#include <ccache/util/format.hpp>
+#include <ccache/util/time.hpp>
#include <algorithm>
#include <cctype>
}
}
+std::string
+format_iso8601_timestamp(const TimePoint& time, TimeZone time_zone)
+{
+ char timestamp[100];
+ const auto tm =
+ (time_zone == TimeZone::local ? util::localtime : util::gmtime)(time);
+ if (tm) {
+ strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", &*tm);
+ } else {
+ snprintf(timestamp,
+ sizeof(timestamp),
+ "%llu",
+ static_cast<long long unsigned int>(time.sec()));
+ }
+ return timestamp;
+}
+
tl::expected<double, std::string>
parse_double(const std::string& value)
{
#pragma once
#include <ccache/util/conversion.hpp>
+#include <ccache/util/timepoint.hpp>
#include <ccache/util/tokenizer.hpp>
#include <nonstd/span.hpp>
// --- Interface ---
enum class SizeUnitPrefixType { binary, decimal };
+enum class TimeZone { local, utc };
// Return true if `suffix` is a suffix of `string`.
bool ends_with(std::string_view string, std::string_view suffix);
std::string format_human_readable_size(uint64_t size,
SizeUnitPrefixType prefix_type);
+// Format `time` as a human-readable ISO8601 timestamp string.
+std::string format_iso8601_timestamp(const TimePoint& time,
+ TimeZone time_zone = TimeZone::local);
+
// Join stringified elements of `container` delimited by `delimiter` into a
// string. There must exist an `std::string to_string(T::value_type)` function.
template<typename T>
-// Copyright (C) 2023 Joel Rosdahl and other contributors
+// Copyright (C) 2023-2025 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
namespace util {
+std::optional<tm>
+gmtime(std::optional<TimePoint> time)
+{
+ time_t timestamp = time ? time->sec() : TimePoint::now().sec();
+#ifdef HAVE_GMTIME_R
+ struct tm result;
+ if (gmtime_r(×tamp, &result)) {
+ return result;
+ }
+#else
+ struct tm* result = ::gmtime(×tamp);
+ if (result) {
+ return *result;
+ }
+#endif
+ return std::nullopt;
+}
+
std::optional<tm>
localtime(std::optional<TimePoint> time)
{
-// Copyright (C) 2023-2024 Joel Rosdahl and other contributors
+// Copyright (C) 2023-2025 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
namespace util {
+// Thread-safe version of `gmtime(3)`. If `time` is not specified the current
+// time of day is used.
+std::optional<tm> gmtime(std::optional<TimePoint> time = {});
+
// Thread-safe version of `localtime(3)`. If `time` is not specified the current
// time of day is used.
std::optional<tm> localtime(std::optional<TimePoint> time = {});
}
}
+TEST_CASE("util::format_iso8601_timestamp")
+{
+ using util::TimePoint;
+ using util::TimeZone;
+
+ CHECK(util::format_iso8601_timestamp(TimePoint(0), TimeZone::utc)
+ == "1970-01-01T00:00:00");
+ CHECK(util::format_iso8601_timestamp(TimePoint(1234567890), TimeZone::utc)
+ == "2009-02-13T23:31:30");
+}
+
TEST_CASE("util::join")
{
{