Rewrite format_common_log

This commit is contained in:
Tatsuhiro Tsujikawa
2025-05-23 20:41:32 +09:00
parent 8b0c12219a
commit d73b5d42e9
3 changed files with 153 additions and 96 deletions

View File

@@ -239,53 +239,6 @@ char *http_date(char *res, time_t t) {
return p;
}
std::string common_log_date(time_t t) {
// 03/Jul/2014:00:19:38 +0900
std::string res(26, 0);
common_log_date(&res[0], t);
return res;
}
char *common_log_date(char *res, time_t t) {
struct tm tms;
if (localtime_r(&t, &tms) == nullptr) {
return res;
}
auto p = res;
p = cpydig2(tms.tm_mday, p);
*p++ = '/';
p = std::ranges::copy(MONTH[tms.tm_mon], p).out;
*p++ = '/';
p = cpydig4(tms.tm_year + 1900, p);
*p++ = ':';
p = cpydig2(tms.tm_hour, p);
*p++ = ':';
p = cpydig2(tms.tm_min, p);
*p++ = ':';
p = cpydig2(tms.tm_sec, p);
*p++ = ' ';
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
auto gmtoff = tms.tm_gmtoff;
#else // !HAVE_STRUCT_TM_TM_GMTOFF
auto gmtoff = nghttp2_timegm(&tms) - t;
#endif // !HAVE_STRUCT_TM_TM_GMTOFF
if (gmtoff >= 0) {
*p++ = '+';
} else {
*p++ = '-';
gmtoff = -gmtoff;
}
p = cpydig2(gmtoff / 3600, p);
p = cpydig2((gmtoff % 3600) / 60, p);
return p;
}
std::string format_iso8601(const std::chrono::system_clock::time_point &tp) {
// 2014-11-15T12:58:24.741Z
// 2014-11-15T12:58:24.741+09:00
@@ -411,6 +364,56 @@ StringRef format_iso8601_basic(char *out,
return {out, p};
}
StringRef format_common_log(char *out,
const std::chrono::system_clock::time_point &tp) {
return format_common_log(out, tp, get_current_time_zone());
}
StringRef format_common_log(char *out,
const std::chrono::system_clock::time_point &tp,
const std::chrono::time_zone *tz) {
auto t = std::chrono::floor<std::chrono::milliseconds>(tp);
auto zt = std::chrono::zoned_time{tz, t};
auto lt = zt.get_local_time();
auto days = std::chrono::floor<std::chrono::days>(lt);
auto ymd = std::chrono::year_month_day{days};
auto p = out;
p = cpydig2(static_cast<uint32_t>(ymd.day()), p);
*p++ = '/';
p = std::ranges::copy(MONTH[static_cast<uint32_t>(ymd.month()) - 1], p).out;
*p++ = '/';
p = cpydig4(static_cast<int>(ymd.year()), p);
*p++ = ':';
auto hms = std::chrono::hh_mm_ss{lt - days};
p = cpydig2(hms.hours().count(), p);
*p++ = ':';
p = cpydig2(hms.minutes().count(), p);
*p++ = ':';
p = cpydig2(hms.seconds().count(), p);
*p++ = ' ';
auto sys_info = zt.get_info();
auto gmtoff =
std::chrono::floor<std::chrono::minutes>(sys_info.offset).count();
if (gmtoff >= 0) {
*p++ = '+';
} else {
*p++ = '-';
gmtoff = -gmtoff;
}
p = cpydig2(gmtoff / 60, p);
p = cpydig2(gmtoff % 60, p);
*p = '\0';
return {out, p};
}
#else // !defined(HAVE_STD_CHRONO_TIME_ZONE)
namespace {
char *iso8601_date(char *out, const std::chrono::system_clock::time_point &tp) {
@@ -522,6 +525,58 @@ format_iso8601_basic(char *out,
*p = '\0';
return {out, p};
}
namespace {
char *common_log_date(char *out,
const std::chrono::system_clock::time_point &tp) {
time_t t =
std::chrono::floor<std::chrono::seconds>(tp.time_since_epoch()).count();
struct tm tms;
if (localtime_r(&t, &tms) == nullptr) {
return out;
}
auto p = out;
p = cpydig2(tms.tm_mday, p);
*p++ = '/';
p = std::ranges::copy(MONTH[tms.tm_mon], p).out;
*p++ = '/';
p = cpydig4(tms.tm_year + 1900, p);
*p++ = ':';
p = cpydig2(tms.tm_hour, p);
*p++ = ':';
p = cpydig2(tms.tm_min, p);
*p++ = ':';
p = cpydig2(tms.tm_sec, p);
*p++ = ' ';
# ifdef HAVE_STRUCT_TM_TM_GMTOFF
auto gmtoff = tms.tm_gmtoff;
# else // !HAVE_STRUCT_TM_TM_GMTOFF
auto gmtoff = nghttp2_timegm(&tms) - t;
# endif // !HAVE_STRUCT_TM_TM_GMTOFF
if (gmtoff >= 0) {
*p++ = '+';
} else {
*p++ = '-';
gmtoff = -gmtoff;
}
p = cpydig2(gmtoff / 3600, p);
p = cpydig2((gmtoff % 3600) / 60, p);
return p;
}
} // namespace
StringRef format_common_log(char *out,
const std::chrono::system_clock::time_point &tp) {
auto p = common_log_date(out, tp);
*p = '\0';
return {out, p};
}
#endif // !defined(HAVE_STD_CHRONO_TIME_ZONE)
time_t parse_http_date(const StringRef &s) {

View File

@@ -499,14 +499,6 @@ std::string http_date(time_t t);
// long. This function returns the one beyond the last position.
char *http_date(char *res, time_t t);
// Returns given time |t| from epoch in Common Log format (e.g.,
// 03/Jul/2014:00:19:38 +0900)
std::string common_log_date(time_t t);
// Writes given time |t| from epoch in Common Log format into the
// buffer pointed by |res|. The buffer must be at least 26 bytes
// long. This function returns the one beyond the last position.
char *common_log_date(char *res, time_t t);
time_t parse_http_date(const StringRef &s);
// Parses time formatted as "MMM DD HH:MM:SS YYYY [GMT]" (e.g., Feb 3
@@ -917,17 +909,18 @@ std::vector<StringRef> split_str(const StringRef &s, char delim, size_t n);
// Writes given time |tp| in Common Log format (e.g.,
// 03/Jul/2014:00:19:38 +0900) in buffer pointed by |out|. The buffer
// must be at least 27 bytes, including terminal NULL byte. Expected
// type of |tp| is std::chrono::time_point. This function returns
// StringRef wrapping the buffer pointed by |out|, and this string is
// terminated by NULL.
template <typename T> StringRef format_common_log(char *out, const T &tp) {
auto t =
std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch());
auto p = common_log_date(out, t.count());
*p = '\0';
return StringRef{out, p};
}
// must be at least 27 bytes, including terminal NULL byte. This
// function returns StringRef wrapping the buffer pointed by |out|,
// and this string is terminated by NULL.
StringRef format_common_log(char *out,
const std::chrono::system_clock::time_point &tp);
#ifdef HAVE_STD_CHRONO_TIME_ZONE
// Works like above but with a given time zone.
StringRef format_common_log(char *out,
const std::chrono::system_clock::time_point &tp,
const std::chrono::time_zone *tz);
#endif // defined(HAVE_STD_CHRONO_TIME_ZONE)
// Returns given time |tp| in ISO 8601 format (e.g.,
// 2014-11-15T12:58:24.741Z or 2014-11-15T12:58:24.741+09:00).

View File

@@ -655,6 +655,51 @@ void test_util_parse_http_date(void) {
}
void test_util_localtime_date(void) {
std::array<char, 30> buf;
#ifdef HAVE_STD_CHRONO_TIME_ZONE
assert_stdsv_equal(
"2001-10-02T00:34:56.123+12:00"sv,
util::format_iso8601(buf.data(),
std::chrono::system_clock::time_point(
std::chrono::milliseconds(1001939696123LL)),
std::chrono::locate_zone("Pacific/Auckland"sv)));
assert_stdsv_equal(
"20011002T003456.123+1200"sv,
util::format_iso8601_basic(buf.data(),
std::chrono::system_clock::time_point(
std::chrono::milliseconds(1001939696123LL)),
std::chrono::locate_zone("Pacific/Auckland"sv)));
assert_stdsv_equal(
"02/Oct/2001:00:34:56 +1200"sv,
util::format_common_log(
buf.data(),
std::chrono::system_clock::time_point(std::chrono::seconds(1001939696)),
std::chrono::locate_zone("Pacific/Auckland"sv)));
assert_stdsv_equal(
"2001-10-01T12:34:56.123Z"sv,
util::format_iso8601(buf.data(),
std::chrono::system_clock::time_point(
std::chrono::milliseconds(1001939696123LL)),
std::chrono::locate_zone("GMT"sv)));
assert_stdsv_equal(
"20011001T123456.123Z"sv,
util::format_iso8601_basic(buf.data(),
std::chrono::system_clock::time_point(
std::chrono::milliseconds(1001939696123LL)),
std::chrono::locate_zone("GMT"sv)));
assert_stdsv_equal(
"01/Oct/2001:12:34:56 +0000"sv,
util::format_common_log(
buf.data(),
std::chrono::system_clock::time_point(std::chrono::seconds(1001939696)),
std::chrono::locate_zone("GMT"sv)));
#else // !defined(HAVE_STD_CHRONO_TIME_ZONE)
auto tz = getenv("TZ");
if (tz) {
tz = strdup(tz);
@@ -666,60 +711,23 @@ void test_util_localtime_date(void) {
# endif // !__linux__
tzset();
assert_stdstring_equal("02/Oct/2001:00:34:56 +1200",
util::common_log_date(1001939696));
assert_stdsv_equal(
"2001-10-02T00:34:56.123+12:00"sv,
util::format_iso8601(buf.data(),
std::chrono::system_clock::time_point(
std::chrono::milliseconds(1001939696123LL))));
std::array<char, 27> common_buf;
assert_stdsv_equal(
"20011002T003456.123+1200"sv,
util::format_iso8601_basic(buf.data(),
std::chrono::system_clock::time_point(
std::chrono::milliseconds(1001939696123LL))));
assert_stdsv_equal("02/Oct/2001:00:34:56 +1200"sv,
util::format_common_log(
common_buf.data(), std::chrono::system_clock::time_point(
assert_stdsv_equal(
"02/Oct/2001:00:34:56 +1200"sv,
util::format_common_log(buf.data(), std::chrono::system_clock::time_point(
std::chrono::seconds(1001939696))));
std::array<char, 30> iso8601_buf;
#ifdef HAVE_STD_CHRONO_TIME_ZONE
assert_stdsv_equal(
"2001-10-02T00:34:56.123+12:00"sv,
util::format_iso8601(iso8601_buf.data(),
std::chrono::system_clock::time_point(
std::chrono::milliseconds(1001939696123LL)),
std::chrono::locate_zone("Pacific/Auckland"sv)));
assert_stdsv_equal(
"20011002T003456.123+1200"sv,
util::format_iso8601_basic(iso8601_buf.data(),
std::chrono::system_clock::time_point(
std::chrono::milliseconds(1001939696123LL)),
std::chrono::locate_zone("Pacific/Auckland"sv)));
assert_stdsv_equal(
"2001-10-01T12:34:56.123Z"sv,
util::format_iso8601(iso8601_buf.data(),
std::chrono::system_clock::time_point(
std::chrono::milliseconds(1001939696123LL)),
std::chrono::locate_zone("GMT"sv)));
assert_stdsv_equal(
"20011001T123456.123Z"sv,
util::format_iso8601_basic(iso8601_buf.data(),
std::chrono::system_clock::time_point(
std::chrono::milliseconds(1001939696123LL)),
std::chrono::locate_zone("GMT"sv)));
#else // !defined(HAVE_STD_CHRONO_TIME_ZONE)
assert_stdsv_equal(
"2001-10-02T00:34:56.123+12:00"sv,
util::format_iso8601(iso8601_buf.data(),
std::chrono::system_clock::time_point(
std::chrono::milliseconds(1001939696123LL))));
assert_stdsv_equal(
"20011002T003456.123+1200"sv,
util::format_iso8601_basic(iso8601_buf.data(),
std::chrono::system_clock::time_point(
std::chrono::milliseconds(1001939696123LL))));
#endif // !defined(HAVE_STD_CHRONO_TIME_ZONE)
if (tz) {
setenv("TZ", tz, 1);
free(tz);
@@ -727,6 +735,7 @@ void test_util_localtime_date(void) {
unsetenv("TZ");
}
tzset();
#endif // !defined(HAVE_STD_CHRONO_TIME_ZONE)
}
void test_util_get_uint64(void) {