Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize tm formatting #2602

Merged
merged 2 commits into from
Nov 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 117 additions & 47 deletions include/fmt/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <ctime>
#include <locale>
#include <sstream>
#include <type_traits>

#include "format.h"

Expand Down Expand Up @@ -871,6 +872,22 @@ inline const char* tm_mon_short_name(int mon) {
return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???";
}

template <typename T, typename = void>
struct has_member_data_tm_gmtoff : std::false_type {};
template <typename T>
struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>>
: std::true_type {};

#if defined(_WIN32)
inline void tzset_once() {
static bool init = []() -> bool {
_tzset();
return true;
}();
ignore_unused(init);
}
#endif

template <typename OutputIt, typename Char> class tm_writer {
private:
static constexpr int days_per_week = 7;
Expand Down Expand Up @@ -988,6 +1005,36 @@ template <typename OutputIt, typename Char> class tm_writer {
}
}

void write_utc_offset(long offset) {
if (offset < 0) {
*out_++ = '-';
offset = -offset;
} else {
*out_++ = '+';
}
offset /= 60;
write2(static_cast<int>(offset / 60));
write2(static_cast<int>(offset % 60));
}
void format_utc_offset_impl(std::true_type) {
write_utc_offset(tm_.tm_gmtoff);
}
void format_utc_offset_impl(std::false_type) {
#if defined(_WIN32)
tzset_once();
long offset = 0;
_get_timezone(&offset);
if (tm_.tm_isdst) {
long dstbias = 0;
_get_dstbias(&dstbias);
offset += dstbias;
}
write_utc_offset(-offset);
#else
format_localized('z');
#endif
}

void format_localized(char format, char modifier = 0) {
out_ = write<Char>(out_, tm_, loc_, format, modifier);
}
Expand Down Expand Up @@ -1018,13 +1065,16 @@ template <typename OutputIt, typename Char> class tm_writer {
format_localized('A');
}
void on_dec0_weekday(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('w', 'O');
write1(tm_wday());
if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday());
format_localized('w', 'O');
}
void on_dec1_weekday(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('u', 'O');
auto wday = tm_wday();
write1(wday == 0 ? days_per_week : wday);
if (is_classic_ || ns == numeric_system::standard) {
auto wday = tm_wday();
write1(wday == 0 ? days_per_week : wday);
} else {
format_localized('u', 'O');
}
}

void on_abbr_month() {
Expand Down Expand Up @@ -1091,53 +1141,69 @@ template <typename OutputIt, typename Char> class tm_writer {
out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
}

void on_utc_offset() { format_localized('z'); }
void on_utc_offset() {
format_utc_offset_impl(has_member_data_tm_gmtoff<std::tm>{});
}
void on_tz_name() { format_localized('Z'); }

void on_year(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('Y', 'E');
write_year(tm_year());
if (is_classic_ || ns == numeric_system::standard)
return write_year(tm_year());
format_localized('Y', 'E');
}
void on_short_year(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('y', 'O');
write2(split_year_lower(tm_year()));
if (is_classic_ || ns == numeric_system::standard)
return write2(split_year_lower(tm_year()));
format_localized('y', 'O');
}
void on_offset_year() {
if (is_classic_) return write2(split_year_lower(tm_year()));
format_localized('y', 'E');
}
void on_offset_year() { format_localized('y', 'E'); }

void on_century(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('C', 'E');
auto year = tm_year();
auto upper = year / 100;
if (year >= -99 && year < 0) {
// Zero upper on negative year.
*out_++ = '-';
*out_++ = '0';
} else if (upper >= 0 && upper < 100) {
write2(static_cast<int>(upper));
if (is_classic_ || ns == numeric_system::standard) {
auto year = tm_year();
auto upper = year / 100;
if (year >= -99 && year < 0) {
// Zero upper on negative year.
*out_++ = '-';
*out_++ = '0';
} else if (upper >= 0 && upper < 100) {
write2(static_cast<int>(upper));
} else {
out_ = write<Char>(out_, upper);
}
} else {
out_ = write<Char>(out_, upper);
format_localized('C', 'E');
}
}

void on_dec_month(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('m', 'O');
write2(tm_mon() + 1);
if (is_classic_ || ns == numeric_system::standard)
return write2(tm_mon() + 1);
format_localized('m', 'O');
}

void on_dec0_week_of_year(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('U', 'O');
write2((tm_yday() + days_per_week - tm_wday()) / days_per_week);
if (is_classic_ || ns == numeric_system::standard)
return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week);
format_localized('U', 'O');
}
void on_dec1_week_of_year(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('W', 'O');
auto wday = tm_wday();
write2((tm_yday() + days_per_week -
(wday == 0 ? (days_per_week - 1) : (wday - 1))) /
days_per_week);
if (is_classic_ || ns == numeric_system::standard) {
auto wday = tm_wday();
write2((tm_yday() + days_per_week -
(wday == 0 ? (days_per_week - 1) : (wday - 1))) /
days_per_week);
} else {
format_localized('W', 'O');
}
}
void on_iso_week_of_year(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('V', 'O');
write2(tm_iso_week_of_year());
if (is_classic_ || ns == numeric_system::standard)
return write2(tm_iso_week_of_year());
format_localized('V', 'O');
}

void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
Expand All @@ -1151,32 +1217,36 @@ template <typename OutputIt, typename Char> class tm_writer {
write2(yday % 100);
}
void on_day_of_month(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('d', 'O');
write2(tm_mday());
if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday());
format_localized('d', 'O');
}
void on_day_of_month_space(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('e', 'O');
auto mday = to_unsigned(tm_mday()) % 100;
const char* d2 = digits2(mday);
*out_++ = mday < 10 ? ' ' : d2[0];
*out_++ = d2[1];
if (is_classic_ || ns == numeric_system::standard) {
auto mday = to_unsigned(tm_mday()) % 100;
const char* d2 = digits2(mday);
*out_++ = mday < 10 ? ' ' : d2[0];
*out_++ = d2[1];
} else {
format_localized('e', 'O');
}
}

void on_24_hour(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('H', 'O');
write2(tm_hour());
if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour());
format_localized('H', 'O');
}
void on_12_hour(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('I', 'O');
write2(tm_hour12());
if (is_classic_ || ns == numeric_system::standard)
return write2(tm_hour12());
format_localized('I', 'O');
}
void on_minute(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('M', 'O');
write2(tm_min());
if (is_classic_ || ns == numeric_system::standard) return write2(tm_min());
format_localized('M', 'O');
}
void on_second(numeric_system ns) {
if (ns != numeric_system::standard) return format_localized('S', 'O');
write2(tm_sec());
if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec());
format_localized('S', 'O');
}

void on_12_hour_time() {
Expand Down
7 changes: 7 additions & 0 deletions test/chrono-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,14 @@ std::string system_strftime(const std::string& format, const std::tm* timeptr,
os.imbue(loc);
facet.put(os, os, ' ', timeptr, format.c_str(),
format.c_str() + format.size());
#ifdef _WIN32
// Workaround a bug in older versions of Universal CRT.
auto str = os.str();
if (str == "-0000") str = "+0000";
return str;
#else
return os.str();
#endif
}

FMT_CONSTEXPR std::tm make_tm(int year, int mon, int mday, int hour, int min,
Expand Down
7 changes: 7 additions & 0 deletions test/xchar-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,14 @@ std::wstring system_wcsftime(const std::wstring& format, const std::tm* timeptr,
os.imbue(loc);
facet.put(os, os, L' ', timeptr, format.c_str(),
format.c_str() + format.size());
#ifdef _WIN32
// Workaround a bug in older versions of Universal CRT.
auto str = os.str();
if (str == L"-0000") str = L"+0000";
return str;
#else
return os.str();
#endif
}

TEST(chrono_test, time_point) {
Expand Down