From 4d9e0ea8029f769c8edb65007150de376b71a6f4 Mon Sep 17 00:00:00 2001 From: Viktar Lukashonak Date: Mon, 24 Jul 2023 01:21:30 +0300 Subject: [PATCH] time conversion between time zones Signed-off-by: Viktar Lukashonak --- include/modules/clock.hpp | 17 ++++-- src/modules/clock.cpp | 119 ++++++++++++++++---------------------- 2 files changed, 63 insertions(+), 73 deletions(-) diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index fab38111..0fcd0af2 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -34,11 +34,18 @@ class Clock final : public ALabel { 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; + auto timezones_text(std::chrono::system_clock::time_point now) -> std::string; /*Calendar properties*/ WeeksSide cldWPos_{WeeksSide::HIDDEN}; + /* + 0 - calendar.format.months + 1 - calendar.format.weekdays + 2 - calendar.format.days + 3 - calendar.format.today + 4 - calendar.format.weeks + 5 - tooltip-format + */ std::map fmtMap_; CldMode cldMode_{CldMode::MONTH}; uint cldMonCols_{3}; // Count of the month in the row @@ -52,8 +59,10 @@ class Clock final : public ALabel { std::string cldMonCached_{}; date::day cldBaseDay_{0}; /*Calendar functions*/ - auto get_calendar(const date::zoned_seconds& now, const date::zoned_seconds& wtime) - -> std::string; + auto get_calendar(const date::year_month_day& today, + const date::year_month_day& ymd, + const date::time_zone* tz) + -> const std::string; /*Clock actions*/ void cldModeSwitch(); void cldShift_up(); diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 59963efd..b36b8258 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -17,9 +17,9 @@ 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), - is_calendar_in_tooltip_(false), - is_timezoned_list_in_tooltip_(false) { + current_time_zone_idx_{0}, + is_calendar_in_tooltip_{false}, + is_timezoned_list_in_tooltip_{false} { if (config_["timezones"].isArray() && !config_["timezones"].empty()) { for (const auto& zone_name : config_["timezones"]) { if (!zone_name.isString()) continue; @@ -35,8 +35,7 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } } else if (config_["timezone"].isString()) { if (config_["timezone"].asString().empty()) - // nullptr means that local time should be shown - time_zones_.push_back(nullptr); + time_zones_.push_back(date::current_zone()); else try { time_zones_.push_back(date::locate_zone(config_["timezone"].asString())); @@ -48,20 +47,22 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) // If all timezones are parsed and no one is good if (!time_zones_.size()) { // nullptr means that local time should be shown - time_zones_.push_back(nullptr); + time_zones_.push_back(date::current_zone()); } // Check if a particular placeholder is present in the tooltip format, to know what to calculate // on update. if (config_["tooltip-format"].isString()) { - std::string trimmed_format = config_["tooltip-format"].asString(); + std::string trimmed_format{config_["tooltip-format"].asString()}; trimmed_format.erase(std::remove_if(trimmed_format.begin(), trimmed_format.end(), [](unsigned char x) { return std::isspace(x); }), trimmed_format.end()); - if (trimmed_format.find("{" + kCalendarPlaceholder + "}") != std::string::npos) { + fmtMap_.insert({5, trimmed_format}); + + if (fmtMap_[5].find("{" + kCalendarPlaceholder + "}") != std::string::npos) { is_calendar_in_tooltip_ = true; } - if (trimmed_format.find("{" + KTimezonedTimeListPlaceholder + "}") != std::string::npos) { + if (fmtMap_[5].find("{" + KTimezonedTimeListPlaceholder + "}") != std::string::npos) { is_timezoned_list_in_tooltip_ = true; } } @@ -158,52 +159,33 @@ waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) } const date::time_zone* waybar::modules::Clock::current_timezone() { - return is_timezone_fixed() ? time_zones_[current_time_zone_idx_] : date::current_zone(); -} - -bool waybar::modules::Clock::is_timezone_fixed() { - return time_zones_[current_time_zone_idx_] != nullptr; + return time_zones_[current_time_zone_idx_]; } auto waybar::modules::Clock::update() -> void { - const auto* time_zone = current_timezone(); - auto now = std::chrono::system_clock::now(); - auto ztime = date::zoned_time{time_zone, date::floor(now)}; + const auto* tz{current_timezone()}; + const date::zoned_time now{ + tz, std::chrono::system_clock::now()}; // Define local time is based on provided time zone + const date::year_month_day today{ + date::floor(now.get_local_time())}; // Convert now to year_month_day + const date::year_month_day shiftedDay{today + cldCurrShift_}; // Shift today + // Define shift local time + const auto shiftedNow{date::make_zoned( + tz, date::local_days(shiftedDay) + + (now.get_local_time() - date::floor(now.get_local_time())))}; - auto shifted_date = date::year_month_day{date::floor(now)} + cldCurrShift_; - if (cldCurrShift_.count()) { - shifted_date = date::year_month_day(shifted_date.year(), shifted_date.month(), date::day(1)); - } - auto now_shifted = date::sys_days{shifted_date} + (now - date::floor(now)); - auto shifted_ztime = date::zoned_time{time_zone, date::floor(now_shifted)}; - - std::string text{""}; - if (!is_timezone_fixed()) { - // As date dep is not fully compatible, prefer fmt - tzset(); - auto localtime = fmt::localtime(std::chrono::system_clock::to_time_t(now)); - text = fmt::format(locale_, fmt::runtime(format_), localtime); - } else { - text = fmt::format(locale_, fmt::runtime(format_), ztime); - } - label_.set_markup(text); + label_.set_markup(fmt::format(locale_, fmt::runtime(format_), now)); if (tooltipEnabled()) { - if (config_["tooltip-format"].isString()) { - std::string calendar_lines{""}; - std::string timezoned_time_lines{""}; - if (is_calendar_in_tooltip_) { - calendar_lines = get_calendar(ztime, shifted_ztime); - } - if (is_timezoned_list_in_tooltip_) { - timezoned_time_lines = timezones_text(&now); - } - auto tooltip_format = config_["tooltip-format"].asString(); - text = fmt::format(locale_, fmt::runtime(tooltip_format), shifted_ztime, - fmt::arg(kCalendarPlaceholder.c_str(), calendar_lines), - fmt::arg(KTimezonedTimeListPlaceholder.c_str(), timezoned_time_lines)); - label_.set_tooltip_markup(text); - } + const std::string tz_text{(is_timezoned_list_in_tooltip_) ? timezones_text(now.get_sys_time()) + : ""}; + const std::string cld_text{(is_calendar_in_tooltip_) ? get_calendar(today, shiftedDay, tz) + : ""}; + + const std::string text{fmt::format(locale_, fmt::runtime(fmtMap_[5]), shiftedNow, + fmt::arg(KTimezonedTimeListPlaceholder.c_str(), tz_text), + fmt::arg(kCalendarPlaceholder.c_str(), cld_text))}; + label_.set_tooltip_markup(text); } // Call parent update @@ -219,15 +201,15 @@ auto waybar::modules::Clock::doAction(const std::string& name) -> void { } // The number of weeks in calendar month layout plus 1 more for calendar titles -unsigned cldRowsInMonth(date::year_month const ym, date::weekday const firstdow) { +const unsigned cldRowsInMonth(const date::year_month& ym, const date::weekday& firstdow) { using namespace date; return static_cast( ceil((weekday{ym / 1} - firstdow) + ((ym / last).day() - day{0})).count()) + 2; } -auto cldGetWeekForLine(date::year_month const ym, date::weekday const firstdow, unsigned const line) - -> const date::year_month_weekday { +auto cldGetWeekForLine(const date::year_month& ym, const date::weekday& firstdow, + unsigned const line) -> const date::year_month_weekday { unsigned index = line - 2; auto sd = date::sys_days{ym / 1}; if (date::weekday{sd} == firstdow) ++index; @@ -235,8 +217,8 @@ auto cldGetWeekForLine(date::year_month const ym, date::weekday const firstdow, return ymdw; } -auto getCalendarLine(date::year_month_day const currDate, date::year_month const ym, - unsigned const line, date::weekday const firstdow, +auto getCalendarLine(const date::year_month_day& currDate, const date::year_month ym, + const unsigned line, const date::weekday& firstdow, const std::locale* const locale_) -> std::string { using namespace date::literals; std::ostringstream res; @@ -318,10 +300,9 @@ auto getCalendarLine(date::year_month_day const currDate, date::year_month const return res.str(); } -auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, - const date::zoned_seconds& wtime) -> std::string { - auto daypoint = date::floor(wtime.get_local_time()); - const auto ymd{date::year_month_day{daypoint}}; +auto waybar::modules::Clock::get_calendar(const date::year_month_day& today, + const date::year_month_day& ymd, + const date::time_zone* tz) -> const std::string { const auto ym{ymd.year() / ymd.month()}; const auto y{ymd.year()}; const auto d{ymd.day()}; @@ -329,9 +310,6 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, const auto maxRows{12 / cldMonCols_}; std::ostringstream os; std::ostringstream tmp; - // get currdate - daypoint = date::floor(now.get_local_time()); - const auto currDate{date::year_month_day{daypoint}}; if (cldMode_ == CldMode::YEAR) { if (y / date::month{1} / 1 == cldYearShift_) @@ -361,6 +339,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, else m = 0u; } + for (auto row{0u}; row < maxRows; ++row) { const auto lines = *std::max_element(std::begin(ml) + (row * cldMonCols_), std::begin(ml) + ((row + 1) * cldMonCols_)); @@ -377,8 +356,9 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, if (line < ml[static_cast(ymTmp.month()) - 1u]) os << fmt::format(fmt::runtime(fmtMap_[4]), (line == 2) - ? date::sys_days{ymTmp / 1} - : date::sys_days{cldGetWeekForLine(ymTmp, firstdow, line)}) + ? date::zoned_seconds{tz, date::local_days{ymTmp / 1}} + : date::zoned_seconds{tz, date::local_days{cldGetWeekForLine( + ymTmp, firstdow, line)}}) << ' '; else os << std::string(cldWnLen_, ' '); @@ -387,7 +367,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, os << fmt::format( fmt::runtime((cldWPos_ != WeeksSide::LEFT || line == 0) ? "{:<{}}" : "{:>{}}"), - getCalendarLine(currDate, ymTmp, line, firstdow, &locale_), + getCalendarLine(today, ymTmp, line, firstdow, &locale_), (cldMonColLen_ + ((line < 2) ? cldWnLen_ : 0))); // Week numbers on the right @@ -397,8 +377,9 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, os << ' ' << fmt::format(fmt::runtime(fmtMap_[4]), (line == 2) - ? date::sys_days{ymTmp / 1} - : date::sys_days{cldGetWeekForLine(ymTmp, firstdow, line)}); + ? date::zoned_seconds{tz, date::local_days{ymTmp / 1}} + : date::zoned_seconds{tz, date::local_days{cldGetWeekForLine( + ymTmp, firstdow, line)}}); else os << std::string(cldWnLen_, ' '); } @@ -421,7 +402,7 @@ auto waybar::modules::Clock::get_calendar(const date::zoned_seconds& now, os << fmt::format( // Apply days format fmt::runtime(fmt::format(fmt::runtime(fmtMap_[2]), tmp.str())), // Apply today format - fmt::arg("today", fmt::format(fmt::runtime(fmtMap_[3]), date::format("%e", ymd.day())))); + fmt::arg("today", fmt::format(fmt::runtime(fmtMap_[3]), date::format("%e", d)))); if (cldMode_ == CldMode::YEAR) cldYearCached_ = os.str(); @@ -457,7 +438,7 @@ void waybar::modules::Clock::tz_down() { current_time_zone_idx_ = current_time_zone_idx_ == 0 ? nr_zones - 1 : current_time_zone_idx_ - 1; } -auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_point* now) +auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_point now) -> std::string { if (time_zones_.size() == 1) { return ""; @@ -471,7 +452,7 @@ auto waybar::modules::Clock::timezones_text(std::chrono::system_clock::time_poin if (!timezone) { timezone = date::current_zone(); } - auto ztime = date::zoned_time{timezone, date::floor(*now)}; + auto ztime = date::zoned_time{timezone, date::floor(now)}; os << fmt::format(locale_, fmt::runtime(format_), ztime) << '\n'; } return os.str();