2024-02-20 13:22:02 +01:00
|
|
|
//
|
|
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
|
|
|
//
|
|
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
//
|
|
|
|
#include "td/telegram/BusinessWorkHours.h"
|
|
|
|
|
|
|
|
#include "td/utils/algorithm.h"
|
|
|
|
#include "td/utils/format.h"
|
2024-03-08 11:03:23 +01:00
|
|
|
#include "td/utils/logging.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
2024-02-20 13:22:02 +01:00
|
|
|
|
|
|
|
namespace td {
|
|
|
|
|
2024-02-27 14:03:34 +01:00
|
|
|
td_api::object_ptr<td_api::businessOpeningHoursInterval>
|
|
|
|
BusinessWorkHours::WorkHoursInterval::get_business_opening_hours_interval_object() const {
|
|
|
|
return td_api::make_object<td_api::businessOpeningHoursInterval>(start_minute_, end_minute_);
|
2024-02-20 13:22:02 +01:00
|
|
|
}
|
|
|
|
|
2024-02-20 13:55:17 +01:00
|
|
|
telegram_api::object_ptr<telegram_api::businessWeeklyOpen>
|
|
|
|
BusinessWorkHours::WorkHoursInterval::get_input_business_weekly_open() const {
|
|
|
|
return telegram_api::make_object<telegram_api::businessWeeklyOpen>(start_minute_, end_minute_);
|
|
|
|
}
|
|
|
|
|
2024-02-20 13:22:02 +01:00
|
|
|
BusinessWorkHours::BusinessWorkHours(telegram_api::object_ptr<telegram_api::businessWorkHours> &&work_hours) {
|
|
|
|
if (work_hours != nullptr) {
|
|
|
|
work_hours_ = transform(work_hours->weekly_open_,
|
|
|
|
[](const telegram_api::object_ptr<telegram_api::businessWeeklyOpen> &weekly_open) {
|
|
|
|
return WorkHoursInterval(weekly_open->start_minute_, weekly_open->end_minute_);
|
|
|
|
});
|
2024-03-08 11:03:23 +01:00
|
|
|
sanitize_work_hours();
|
2024-02-20 13:22:02 +01:00
|
|
|
time_zone_id_ = std::move(work_hours->timezone_id_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-27 14:03:34 +01:00
|
|
|
BusinessWorkHours::BusinessWorkHours(td_api::object_ptr<td_api::businessOpeningHours> &&work_hours) {
|
2024-02-20 13:55:17 +01:00
|
|
|
if (work_hours != nullptr) {
|
2024-02-27 14:03:34 +01:00
|
|
|
work_hours_ = transform(work_hours->opening_hours_,
|
|
|
|
[](const td_api::object_ptr<td_api::businessOpeningHoursInterval> &interval) {
|
|
|
|
return WorkHoursInterval(interval->start_minute_, interval->end_minute_);
|
|
|
|
});
|
2024-03-08 11:03:23 +01:00
|
|
|
sanitize_work_hours();
|
2024-02-20 13:55:17 +01:00
|
|
|
time_zone_id_ = std::move(work_hours->time_zone_id_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-20 13:22:02 +01:00
|
|
|
bool BusinessWorkHours::is_empty() const {
|
|
|
|
return work_hours_.empty();
|
|
|
|
}
|
|
|
|
|
2024-02-27 14:03:34 +01:00
|
|
|
td_api::object_ptr<td_api::businessOpeningHours> BusinessWorkHours::get_business_opening_hours_object() const {
|
2024-02-20 13:22:02 +01:00
|
|
|
if (is_empty()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2024-03-08 12:14:28 +01:00
|
|
|
vector<td_api::object_ptr<td_api::businessOpeningHoursInterval>> intervals;
|
|
|
|
for (const auto &work_hour : work_hours_) {
|
|
|
|
auto interval = work_hour;
|
|
|
|
while (interval.start_minute_ / (24 * 60) + 1 < interval.end_minute_ / (24 * 60)) {
|
|
|
|
auto prefix = interval;
|
|
|
|
prefix.end_minute_ = (interval.start_minute_ / (24 * 60) + 1) * 24 * 60;
|
|
|
|
interval.start_minute_ = prefix.end_minute_;
|
|
|
|
intervals.push_back(prefix.get_business_opening_hours_interval_object());
|
|
|
|
}
|
|
|
|
intervals.push_back(interval.get_business_opening_hours_interval_object());
|
|
|
|
}
|
|
|
|
return td_api::make_object<td_api::businessOpeningHours>(time_zone_id_, std::move(intervals));
|
2024-02-20 13:22:02 +01:00
|
|
|
}
|
|
|
|
|
2024-02-20 13:55:17 +01:00
|
|
|
telegram_api::object_ptr<telegram_api::businessWorkHours> BusinessWorkHours::get_input_business_work_hours() const {
|
|
|
|
if (is_empty()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return telegram_api::make_object<telegram_api::businessWorkHours>(
|
|
|
|
0, false, time_zone_id_, transform(work_hours_, [](const WorkHoursInterval &interval) {
|
|
|
|
return interval.get_input_business_weekly_open();
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2024-03-08 11:03:23 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-20 13:22:02 +01:00
|
|
|
bool operator==(const BusinessWorkHours::WorkHoursInterval &lhs, const BusinessWorkHours::WorkHoursInterval &rhs) {
|
|
|
|
return lhs.start_minute_ == rhs.start_minute_ && lhs.end_minute_ == rhs.end_minute_;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator!=(const BusinessWorkHours::WorkHoursInterval &lhs, const BusinessWorkHours::WorkHoursInterval &rhs) {
|
|
|
|
return !(lhs == rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
StringBuilder &operator<<(StringBuilder &string_builder, const BusinessWorkHours::WorkHoursInterval &interval) {
|
|
|
|
return string_builder << '[' << interval.start_minute_ << ',' << interval.end_minute_ << ')';
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator==(const BusinessWorkHours &lhs, const BusinessWorkHours &rhs) {
|
|
|
|
return lhs.work_hours_ == rhs.work_hours_ && lhs.time_zone_id_ == rhs.time_zone_id_;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator!=(const BusinessWorkHours &lhs, const BusinessWorkHours &rhs) {
|
|
|
|
return !(lhs == rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
StringBuilder &operator<<(StringBuilder &string_builder, const BusinessWorkHours &work_hours) {
|
|
|
|
return string_builder << "BusinessWorkHours[" << work_hours.work_hours_ << " in " << work_hours.time_zone_id_ << ']';
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace td
|