diff --git a/td/telegram/BusinessWorkHours.cpp b/td/telegram/BusinessWorkHours.cpp index 59728587d..67bf4773b 100644 --- a/td/telegram/BusinessWorkHours.cpp +++ b/td/telegram/BusinessWorkHours.cpp @@ -8,6 +8,9 @@ #include "td/utils/algorithm.h" #include "td/utils/format.h" +#include "td/utils/logging.h" + +#include namespace td { @@ -27,6 +30,7 @@ BusinessWorkHours::BusinessWorkHours(telegram_api::object_ptr &weekly_open) { return WorkHoursInterval(weekly_open->start_minute_, weekly_open->end_minute_); }); + sanitize_work_hours(); time_zone_id_ = std::move(work_hours->timezone_id_); } } @@ -37,6 +41,7 @@ BusinessWorkHours::BusinessWorkHours(td_api::object_ptr &interval) { return WorkHoursInterval(interval->start_minute_, interval->end_minute_); }); + sanitize_work_hours(); time_zone_id_ = std::move(work_hours->time_zone_id_); } } @@ -65,6 +70,80 @@ telegram_api::object_ptr BusinessWorkHours::get })); } +void BusinessWorkHours::sanitize_work_hours() { + // remove invalid work hour intervals + td::remove_if(work_hours_, [](const WorkHoursInterval &interval) { + if (interval.start_minute_ >= interval.end_minute_ || interval.start_minute_ < 0 || + interval.end_minute_ > 8 * 24 * 60) { + LOG(INFO) << "Ignore interval " << interval; + return true; + } + return false; + }); + + combine_work_hour_intervals(); +} + +void BusinessWorkHours::combine_work_hour_intervals() { + if (work_hours_.empty()) { + return; + } + + // sort intervals + std::sort(work_hours_.begin(), work_hours_.end(), [](const WorkHoursInterval &lhs, const WorkHoursInterval &rhs) { + return lhs.start_minute_ < rhs.start_minute_; + }); + + // combine intersecting intervals + size_t j = 0; + for (size_t i = 1; i < work_hours_.size(); i++) { + CHECK(work_hours_[i].start_minute_ >= work_hours_[j].start_minute_); + if (work_hours_[i].start_minute_ <= work_hours_[j].end_minute_) { + work_hours_[j].end_minute_ = max(work_hours_[j].end_minute_, work_hours_[i].end_minute_); + } else { + work_hours_[++j] = work_hours_[i]; + } + } + work_hours_.resize(j + 1); + + // there must be no intervals longer than 1 week + for (auto &interval : work_hours_) { + interval.end_minute_ = min(interval.end_minute_, interval.start_minute_ + 7 * 24 * 60); + } + + CHECK(!work_hours_.empty()); + + // if the last interval can be exactly merged with the first one, merge them + if (work_hours_[0].start_minute_ != 0 && + work_hours_[0].start_minute_ + 7 * 24 * 60 == work_hours_.back().end_minute_) { + if (work_hours_.back().start_minute_ >= 7 * 24 * 60) { + work_hours_[0].start_minute_ = work_hours_.back().start_minute_ - 7 * 24 * 60; + work_hours_.pop_back(); + CHECK(!work_hours_.empty()); + } else { + work_hours_[0].start_minute_ = 0; + work_hours_.back().end_minute_ = 7 * 24 * 60; + } + } + + // if there are intervals that intersect the first interval or start after the end of the week, + // then they must be normalized + auto max_minute = work_hours_[0].start_minute_ + 7 * 24 * 60; + if (work_hours_.back().end_minute_ > max_minute || work_hours_.back().start_minute_ >= 7 * 24 * 60) { + auto size = work_hours_.size(); + for (size_t i = 1; i < size; i++) { + if (work_hours_[i].start_minute_ >= 7 * 24 * 60) { + work_hours_[i].start_minute_ -= 7 * 24 * 60; + work_hours_[i].end_minute_ -= 7 * 24 * 60; + } else if (work_hours_[i].end_minute_ > max_minute) { + work_hours_.emplace_back(max_minute - 7 * 24 * 60, work_hours_[i].end_minute_ - 7 * 24 * 60); + work_hours_[i].end_minute_ = max_minute; + } + } + combine_work_hour_intervals(); + } +} + bool operator==(const BusinessWorkHours::WorkHoursInterval &lhs, const BusinessWorkHours::WorkHoursInterval &rhs) { return lhs.start_minute_ == rhs.start_minute_ && lhs.end_minute_ == rhs.end_minute_; } diff --git a/td/telegram/BusinessWorkHours.h b/td/telegram/BusinessWorkHours.h index 4fcdda277..b386334dc 100644 --- a/td/telegram/BusinessWorkHours.h +++ b/td/telegram/BusinessWorkHours.h @@ -54,6 +54,10 @@ class BusinessWorkHours { void parse(ParserT &parser); }; + void sanitize_work_hours(); + + void combine_work_hour_intervals(); + friend bool operator==(const WorkHoursInterval &lhs, const WorkHoursInterval &rhs); friend bool operator!=(const WorkHoursInterval &lhs, const WorkHoursInterval &rhs);