refactor(clock): remove struct waybar_time

The structure was used to pass the locale instance to the date
formatter. All the supported versions of `fmt` are passing the locale
parameter via `FormatContext.locale()` so we can remove the struct and
simplify the code.

While we at it, drop `date::make_zoned` in favor of CTAD on a
`date::zoned_time` constructor.
pull/1959/head
Aleksei Bavshin 2023-01-16 16:48:30 -08:00
parent ba498869c5
commit 67efe1af89
No known key found for this signature in database
GPG Key ID: 4F071603387A382A
6 changed files with 117 additions and 133 deletions

View File

@ -5,11 +5,7 @@
#include "ALabel.hpp" #include "ALabel.hpp"
#include "util/sleeper_thread.hpp" #include "util/sleeper_thread.hpp"
namespace waybar { namespace waybar::modules {
struct waybar_time;
namespace modules {
const std::string kCalendarPlaceholder = "calendar"; const std::string kCalendarPlaceholder = "calendar";
const std::string KTimezonedTimeListPlaceholder = "timezoned_time_list"; const std::string KTimezonedTimeListPlaceholder = "timezoned_time_list";
@ -36,12 +32,11 @@ class Clock : public ALabel {
std::string fmt_str_weeks_; std::string fmt_str_weeks_;
std::string fmt_str_calendar_; std::string fmt_str_calendar_;
int fmt_weeks_left_pad_{0}; int fmt_weeks_left_pad_{0};
auto calendar_text(const waybar_time& wtime) -> std::string; auto calendar_text(const date::zoned_seconds& ztime) -> std::string;
auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void; auto weekdays_header(const date::weekday& first_dow, std::ostream& os) -> void;
auto first_day_of_week() -> date::weekday; auto first_day_of_week() -> date::weekday;
const date::time_zone* current_timezone(); const date::time_zone* current_timezone();
bool is_timezone_fixed(); bool is_timezone_fixed();
auto timezones_text(std::chrono::system_clock::time_point* now) -> std::string; auto timezones_text(std::chrono::system_clock::time_point* now) -> std::string;
}; };
} // namespace modules } // namespace waybar::modules
} // namespace waybar

View File

@ -3,17 +3,8 @@
#include <date/tz.h> #include <date/tz.h>
#include <fmt/format.h> #include <fmt/format.h>
namespace waybar { template <typename Duration, typename TimeZonePtr>
struct fmt::formatter<date::zoned_time<Duration, TimeZonePtr>> {
struct waybar_time {
std::locale locale;
date::zoned_seconds ztime;
};
} // namespace waybar
template <>
struct fmt::formatter<waybar::waybar_time> {
std::string_view specs; std::string_view specs;
template <typename ParseContext> template <typename ParseContext>
@ -33,7 +24,11 @@ struct fmt::formatter<waybar::waybar_time> {
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const waybar::waybar_time& t, FormatContext& ctx) { auto format(const date::zoned_time<Duration, TimeZonePtr>& ztime, FormatContext& ctx) {
return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(specs), t.ztime)); if (ctx.locale()) {
const auto loc = ctx.locale().template get<std::locale>();
return fmt::format_to(ctx.out(), "{}", date::format(loc, fmt::to_string(specs), ztime));
}
return fmt::format_to(ctx.out(), "{}", date::format(fmt::to_string(specs), ztime));
} }
}; };

View File

@ -9,15 +9,13 @@
#include <sstream> #include <sstream>
#include <type_traits> #include <type_traits>
#include "util/date.hpp"
#include "util/ustring_clen.hpp" #include "util/ustring_clen.hpp"
#include "util/waybar_time.hpp"
#ifdef HAVE_LANGINFO_1STDAY #ifdef HAVE_LANGINFO_1STDAY
#include <langinfo.h> #include <langinfo.h>
#include <locale.h> #include <locale.h>
#endif #endif
using waybar::waybar_time;
waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config)
: ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true),
current_time_zone_idx_(0), current_time_zone_idx_(0),
@ -110,15 +108,13 @@ bool waybar::modules::Clock::is_timezone_fixed() {
} }
auto waybar::modules::Clock::update() -> void { auto waybar::modules::Clock::update() -> void {
auto time_zone = current_timezone(); const auto* time_zone = current_timezone();
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
waybar_time wtime = {locale_, auto ztime = date::zoned_time{time_zone, date::floor<std::chrono::seconds>(now)};
date::make_zoned(time_zone, date::floor<std::chrono::seconds>(now))};
auto shifted_date = date::year_month_day{date::floor<date::days>(now)} + calendar_shift_; auto shifted_date = date::year_month_day{date::floor<date::days>(now)} + calendar_shift_;
auto now_shifted = date::sys_days{shifted_date} + (now - date::floor<date::days>(now)); auto now_shifted = date::sys_days{shifted_date} + (now - date::floor<date::days>(now));
waybar_time shifted_wtime = { auto shifted_ztime = date::zoned_time{time_zone, date::floor<std::chrono::seconds>(now_shifted)};
locale_, date::make_zoned(time_zone, date::floor<std::chrono::seconds>(now_shifted))};
std::string text = ""; std::string text = "";
if (!is_timezone_fixed()) { if (!is_timezone_fixed()) {
@ -127,7 +123,7 @@ auto waybar::modules::Clock::update() -> void {
auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now)); auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now));
text = fmt::format(locale_, format_, localtime); text = fmt::format(locale_, format_, localtime);
} else { } else {
text = fmt::format(format_, wtime); text = fmt::format(locale_, format_, ztime);
} }
label_.set_markup(text); label_.set_markup(text);
@ -136,13 +132,13 @@ auto waybar::modules::Clock::update() -> void {
std::string calendar_lines{""}; std::string calendar_lines{""};
std::string timezoned_time_lines{""}; std::string timezoned_time_lines{""};
if (is_calendar_in_tooltip_) { if (is_calendar_in_tooltip_) {
calendar_lines = calendar_text(shifted_wtime); calendar_lines = calendar_text(shifted_ztime);
} }
if (is_timezoned_list_in_tooltip_) { if (is_timezoned_list_in_tooltip_) {
timezoned_time_lines = timezones_text(&now); timezoned_time_lines = timezones_text(&now);
} }
auto tooltip_format = config_["tooltip-format"].asString(); auto tooltip_format = config_["tooltip-format"].asString();
text = fmt::format(tooltip_format, shifted_wtime, text = fmt::format(locale_, tooltip_format, shifted_ztime,
fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines), fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines),
fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines)); fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines));
label_.set_tooltip_markup(text); label_.set_tooltip_markup(text);
@ -190,8 +186,8 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll* e) {
return true; return true;
} }
auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string { auto waybar::modules::Clock::calendar_text(const date::zoned_seconds& ztime) -> std::string {
const auto daypoint = date::floor<date::days>(wtime.ztime.get_local_time()); const auto daypoint = date::floor<date::days>(ztime.get_local_time());
const auto ymd{date::year_month_day{daypoint}}; const auto ymd{date::year_month_day{daypoint}};
if (calendar_cached_ymd_ == ymd) { if (calendar_cached_ymd_ == ymd) {
@ -318,7 +314,6 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin
return ""; return "";
} }
std::stringstream os; std::stringstream os;
waybar_time wtime;
for (size_t time_zone_idx = 0; time_zone_idx < time_zones_.size(); ++time_zone_idx) { for (size_t time_zone_idx = 0; time_zone_idx < time_zones_.size(); ++time_zone_idx) {
if (static_cast<int>(time_zone_idx) == current_time_zone_idx_) { if (static_cast<int>(time_zone_idx) == current_time_zone_idx_) {
continue; continue;
@ -327,8 +322,8 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin
if (!timezone) { if (!timezone) {
timezone = date::current_zone(); timezone = date::current_zone();
} }
wtime = {locale_, date::make_zoned(timezone, date::floor<std::chrono::seconds>(*now))}; auto ztime = date::zoned_time{timezone, date::floor<std::chrono::seconds>(*now)};
os << fmt::format(format_, wtime) << '\n'; os << fmt::format(locale_, format_, ztime) << '\n';
} }
return os.str(); return os.str();
} }

94
test/date.cpp 100644
View File

@ -0,0 +1,94 @@
#include "util/date.hpp"
#if __has_include(<catch2/catch_test_macros.hpp>)
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_all.hpp>
#else
#include <catch2/catch.hpp>
#endif
#include <chrono>
#include <stdexcept>
using namespace std::literals::chrono_literals;
/*
* Check that the date/time formatter with locale and timezone support is working as expected.
*/
const date::zoned_time<std::chrono::seconds> TEST_TIME = date::zoned_time{
"UTC", date::local_days{date::Monday[1] / date::January / 2022} + 13h + 4min + 5s};
TEST_CASE("Format UTC time", "[clock][util]") {
const auto loc = std::locale("C");
const auto tm = TEST_TIME;
REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified
REQUIRE(fmt::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC");
REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
/* Test a few locales that are most likely to be present */
SECTION("US locale") {
try {
const auto loc = std::locale("en_US");
REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified
REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704
Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM"));
REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "01/03/2022 01:04:05 PM");
REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
} catch (const std::runtime_error&) {
// locale not found; ignore
}
}
SECTION("GB locale") {
try {
const auto loc = std::locale("en_GB");
REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified
REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704
Catch::Matchers::StartsWith("Mon 03 Jan 2022 13:04:05"));
REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "03/01/22 13:04:05");
REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
} catch (const std::runtime_error&) {
// locale not found; ignore
}
}
}
TEST_CASE("Format zoned time", "[clock][util]") {
const auto loc = std::locale("C");
const auto tm = date::zoned_time{"America/New_York", TEST_TIME};
REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified
REQUIRE(fmt::format(loc, "{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST");
REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
/* Test a few locales that are most likely to be present */
SECTION("US locale") {
try {
const auto loc = std::locale("en_US");
REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified
REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704
Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM"));
REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "01/03/2022 08:04:05 AM");
REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
} catch (const std::runtime_error&) {
// locale not found; ignore
}
}
SECTION("GB locale") {
try {
const auto loc = std::locale("en_GB");
REQUIRE(fmt::format(loc, "{}", tm).empty()); // no format specified
REQUIRE_THAT(fmt::format(loc, "{:%c}", tm), // HowardHinnant/date#704
Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05"));
REQUIRE(fmt::format(loc, "{:%x %X}", tm) == "03/01/22 08:04:05");
REQUIRE(fmt::format(loc, "{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
} catch (const std::runtime_error&) {
// locale not found; ignore
}
}
}

View File

@ -15,7 +15,7 @@ test_src = files(
if tz_dep.found() if tz_dep.found()
test_dep += tz_dep test_dep += tz_dep
test_src += files('waybar_time.cpp') test_src += files('date.cpp')
endif endif
waybar_test = executable( waybar_test = executable(

View File

@ -1,95 +0,0 @@
#include "util/waybar_time.hpp"
#include <date/date.h>
#include <date/tz.h>
#if __has_include(<catch2/catch_test_macros.hpp>)
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_all.hpp>
#else
#include <catch2/catch.hpp>
#endif
#include <chrono>
#include <stdexcept>
using namespace std::literals::chrono_literals;
/*
* Check that the date/time formatter with locale and timezone support is working as expected.
*/
const date::zoned_time<std::chrono::seconds> TEST_TIME = date::make_zoned(
"UTC", date::local_days{date::Monday[1] / date::January / 2022} + 13h + 4min + 5s);
TEST_CASE("Format UTC time", "[clock][util]") {
waybar::waybar_time tm{std::locale("C"), TEST_TIME};
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 13:04:05 2022 UTC");
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
/* Test a few locales that are most likely to be present */
SECTION("US locale") {
try {
tm.locale = std::locale("en_US");
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
Catch::Matchers::StartsWith("Mon 03 Jan 2022 01:04:05 PM"));
REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 01:04:05 PM");
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
} catch (const std::runtime_error&) {
// locale not found; ignore
}
}
SECTION("GB locale") {
try {
tm.locale = std::locale("en_GB");
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
Catch::Matchers::StartsWith("Mon 03 Jan 2022 13:04:05"));
REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 13:04:05");
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103130405");
} catch (const std::runtime_error&) {
// locale not found; ignore
}
}
}
TEST_CASE("Format zoned time", "[clock][util]") {
waybar::waybar_time tm{std::locale("C"), date::make_zoned("America/New_York", TEST_TIME)};
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
REQUIRE(fmt::format("{:%c %Z}", tm) == "Mon Jan 3 08:04:05 2022 EST");
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
/* Test a few locales that are most likely to be present */
SECTION("US locale") {
try {
tm.locale = std::locale("en_US");
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05 AM"));
REQUIRE(fmt::format("{:%x %X}", tm) == "01/03/2022 08:04:05 AM");
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
} catch (const std::runtime_error&) {
// locale not found; ignore
}
}
SECTION("GB locale") {
try {
tm.locale = std::locale("en_GB");
REQUIRE(fmt::format("{}", tm).empty()); // no format specified
REQUIRE_THAT(fmt::format("{:%c}", tm), // HowardHinnant/date#704
Catch::Matchers::StartsWith("Mon 03 Jan 2022 08:04:05"));
REQUIRE(fmt::format("{:%x %X}", tm) == "03/01/22 08:04:05");
REQUIRE(fmt::format("{arg:%Y%m%d%H%M%S}", fmt::arg("arg", tm)) == "20220103080405");
} catch (const std::runtime_error&) {
// locale not found; ignore
}
}
}