From 67efe1af892cff2cc37f9aeb0fd7420b7d4eb621 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 16 Jan 2023 16:48:30 -0800 Subject: [PATCH] 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. --- include/modules/clock.hpp | 11 +-- include/util/{waybar_time.hpp => date.hpp} | 21 ++--- src/modules/clock.cpp | 27 +++--- test/date.cpp | 94 +++++++++++++++++++++ test/meson.build | 2 +- test/waybar_time.cpp | 95 ---------------------- 6 files changed, 117 insertions(+), 133 deletions(-) rename include/util/{waybar_time.hpp => date.hpp} (51%) create mode 100644 test/date.cpp delete mode 100644 test/waybar_time.cpp diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index ef129fbd..c97565de 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -5,11 +5,7 @@ #include "ALabel.hpp" #include "util/sleeper_thread.hpp" -namespace waybar { - -struct waybar_time; - -namespace modules { +namespace waybar::modules { const std::string kCalendarPlaceholder = "calendar"; const std::string KTimezonedTimeListPlaceholder = "timezoned_time_list"; @@ -36,12 +32,11 @@ class Clock : public ALabel { std::string fmt_str_weeks_; std::string fmt_str_calendar_; 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 first_day_of_week() -> date::weekday; const date::time_zone* current_timezone(); bool is_timezone_fixed(); auto timezones_text(std::chrono::system_clock::time_point* now) -> std::string; }; -} // namespace modules -} // namespace waybar +} // namespace waybar::modules diff --git a/include/util/waybar_time.hpp b/include/util/date.hpp similarity index 51% rename from include/util/waybar_time.hpp rename to include/util/date.hpp index b9f9ea98..ec948bd5 100644 --- a/include/util/waybar_time.hpp +++ b/include/util/date.hpp @@ -3,17 +3,8 @@ #include #include -namespace waybar { - -struct waybar_time { - std::locale locale; - date::zoned_seconds ztime; -}; - -} // namespace waybar - -template <> -struct fmt::formatter { +template +struct fmt::formatter> { std::string_view specs; template @@ -33,7 +24,11 @@ struct fmt::formatter { } template - auto format(const waybar::waybar_time& t, FormatContext& ctx) { - return format_to(ctx.out(), "{}", date::format(t.locale, fmt::to_string(specs), t.ztime)); + auto format(const date::zoned_time& ztime, FormatContext& ctx) { + if (ctx.locale()) { + const auto loc = ctx.locale().template get(); + 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)); } }; diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 9871024c..360b7467 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -9,15 +9,13 @@ #include #include +#include "util/date.hpp" #include "util/ustring_clen.hpp" -#include "util/waybar_time.hpp" #ifdef HAVE_LANGINFO_1STDAY #include #include #endif -using waybar::waybar_time; - waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) : ALabel(config, "clock", id, "{:%H:%M}", 60, false, false, true), current_time_zone_idx_(0), @@ -110,15 +108,13 @@ bool waybar::modules::Clock::is_timezone_fixed() { } auto waybar::modules::Clock::update() -> void { - auto time_zone = current_timezone(); + const auto* time_zone = current_timezone(); auto now = std::chrono::system_clock::now(); - waybar_time wtime = {locale_, - date::make_zoned(time_zone, date::floor(now))}; + auto ztime = date::zoned_time{time_zone, date::floor(now)}; auto shifted_date = date::year_month_day{date::floor(now)} + calendar_shift_; auto now_shifted = date::sys_days{shifted_date} + (now - date::floor(now)); - waybar_time shifted_wtime = { - locale_, date::make_zoned(time_zone, date::floor(now_shifted))}; + auto shifted_ztime = date::zoned_time{time_zone, date::floor(now_shifted)}; std::string text = ""; 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)); text = fmt::format(locale_, format_, localtime); } else { - text = fmt::format(format_, wtime); + text = fmt::format(locale_, format_, ztime); } label_.set_markup(text); @@ -136,13 +132,13 @@ auto waybar::modules::Clock::update() -> void { std::string calendar_lines{""}; std::string timezoned_time_lines{""}; if (is_calendar_in_tooltip_) { - calendar_lines = calendar_text(shifted_wtime); + calendar_lines = calendar_text(shifted_ztime); } if (is_timezoned_list_in_tooltip_) { timezoned_time_lines = timezones_text(&now); } 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(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines)); label_.set_tooltip_markup(text); @@ -190,8 +186,8 @@ bool waybar::modules::Clock::handleScroll(GdkEventScroll* e) { return true; } -auto waybar::modules::Clock::calendar_text(const waybar_time& wtime) -> std::string { - const auto daypoint = date::floor(wtime.ztime.get_local_time()); +auto waybar::modules::Clock::calendar_text(const date::zoned_seconds& ztime) -> std::string { + const auto daypoint = date::floor(ztime.get_local_time()); const auto ymd{date::year_month_day{daypoint}}; if (calendar_cached_ymd_ == ymd) { @@ -318,7 +314,6 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin return ""; } std::stringstream os; - waybar_time wtime; for (size_t time_zone_idx = 0; time_zone_idx < time_zones_.size(); ++time_zone_idx) { if (static_cast(time_zone_idx) == current_time_zone_idx_) { continue; @@ -327,8 +322,8 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin if (!timezone) { timezone = date::current_zone(); } - wtime = {locale_, date::make_zoned(timezone, date::floor(*now))}; - os << fmt::format(format_, wtime) << '\n'; + auto ztime = date::zoned_time{timezone, date::floor(*now)}; + os << fmt::format(locale_, format_, ztime) << '\n'; } return os.str(); } diff --git a/test/date.cpp b/test/date.cpp new file mode 100644 index 00000000..704feb22 --- /dev/null +++ b/test/date.cpp @@ -0,0 +1,94 @@ +#include "util/date.hpp" + +#if __has_include() +#include +#include +#else +#include +#endif +#include +#include + +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 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 + } + } +} diff --git a/test/meson.build b/test/meson.build index b1e11237..02cbb2a4 100644 --- a/test/meson.build +++ b/test/meson.build @@ -15,7 +15,7 @@ test_src = files( if tz_dep.found() test_dep += tz_dep - test_src += files('waybar_time.cpp') + test_src += files('date.cpp') endif waybar_test = executable( diff --git a/test/waybar_time.cpp b/test/waybar_time.cpp deleted file mode 100644 index 9f9f5dc5..00000000 --- a/test/waybar_time.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "util/waybar_time.hpp" - -#include -#include - -#if __has_include() -#include -#include -#else -#include -#endif -#include -#include - -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 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 - } - } -}