Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
26
3party/opening_hours/CMakeLists.txt
Normal file
26
3party/opening_hours/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
project(opening_hours)
|
||||
|
||||
set(SRC
|
||||
opening_hours.hpp
|
||||
opening_hours.cpp
|
||||
opening_hours_parsers.hpp
|
||||
opening_hours_parsers_terminals.cpp
|
||||
parse_opening_hours.hpp
|
||||
parse_opening_hours.cpp
|
||||
parse_years.cpp
|
||||
parse_weekdays.cpp
|
||||
parse_weeks.cpp
|
||||
parse_timespans.cpp
|
||||
parse_months.cpp
|
||||
rules_evaluation_private.hpp
|
||||
rules_evaluation.hpp
|
||||
rules_evaluation.cpp
|
||||
)
|
||||
|
||||
omim_add_library(${PROJECT_NAME} ${SRC})
|
||||
|
||||
target_include_directories(${PROJECT_NAME} INTERFACE .)
|
||||
|
||||
omim_add_test_subdirectory(opening_hours_tests)
|
||||
omim_add_test_subdirectory(opening_hours_integration_tests)
|
||||
omim_add_test_subdirectory(opening_hours_supported_features_tests)
|
||||
977
3party/opening_hours/opening_hours.cpp
Normal file
977
3party/opening_hours/opening_hours.cpp
Normal file
|
|
@ -0,0 +1,977 @@
|
|||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Mail.Ru Group
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "opening_hours.hpp"
|
||||
#include "rules_evaluation.hpp"
|
||||
#include "parse_opening_hours.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T, typename SeparatorExtractor>
|
||||
void PrintVector(std::ostream & ost, std::vector<T> const & v,
|
||||
SeparatorExtractor && sepFunc)
|
||||
{
|
||||
auto it = begin(v);
|
||||
if (it == end(v))
|
||||
return;
|
||||
|
||||
auto sep = sepFunc(*it);
|
||||
ost << *it++;
|
||||
while (it != end(v))
|
||||
{
|
||||
ost << sep << *it;
|
||||
sep = sepFunc(*it);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PrintVector(std::ostream & ost, std::vector<T> const & v, char const * const sep = ", ")
|
||||
{
|
||||
PrintVector(ost, v, [&sep](T const &) { return sep; });
|
||||
}
|
||||
|
||||
void PrintOffset(std::ostream & ost, int32_t const offset, bool const space)
|
||||
{
|
||||
if (offset == 0)
|
||||
return;
|
||||
|
||||
if (space)
|
||||
ost << ' ';
|
||||
if (offset > 0)
|
||||
ost << '+';
|
||||
ost << offset;
|
||||
ost << ' ' << "day";
|
||||
if (std::abs(offset) > 1)
|
||||
ost << 's';
|
||||
}
|
||||
|
||||
class StreamFlagsKeeper
|
||||
{
|
||||
public:
|
||||
explicit StreamFlagsKeeper(std::ostream & ost):
|
||||
m_ost(ost),
|
||||
m_flags(m_ost.flags())
|
||||
{
|
||||
}
|
||||
|
||||
~StreamFlagsKeeper()
|
||||
{
|
||||
m_ost.flags(m_flags);
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream & m_ost;
|
||||
std::ios_base::fmtflags m_flags;
|
||||
};
|
||||
|
||||
|
||||
template <typename TNumber>
|
||||
void PrintPaddedNumber(std::ostream & ost, TNumber const number, uint32_t const padding = 1)
|
||||
{
|
||||
static constexpr bool isChar = std::is_same_v<signed char, TNumber> ||
|
||||
std::is_same_v<unsigned char, TNumber> ||
|
||||
std::is_same_v<char, TNumber>;
|
||||
|
||||
if constexpr (isChar)
|
||||
{
|
||||
PrintPaddedNumber(ost, static_cast<int32_t>(number), padding);
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(std::is_integral<TNumber>::value, "number should be of integral type.");
|
||||
StreamFlagsKeeper keeper(ost);
|
||||
ost << std::setw(padding) << std::setfill('0') << number;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHoursMinutes(std::ostream & ost,
|
||||
std::chrono::hours::rep hours,
|
||||
std::chrono::minutes::rep minutes)
|
||||
{
|
||||
PrintPaddedNumber(ost, hours, 2);
|
||||
ost << ':';
|
||||
PrintPaddedNumber(ost, minutes, 2);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
|
||||
// HourMinutes -------------------------------------------------------------------------------------
|
||||
|
||||
bool HourMinutes::IsExtended() const
|
||||
{
|
||||
return GetDuration() > 24_h;
|
||||
}
|
||||
|
||||
void HourMinutes::SetHours(THours const hours)
|
||||
{
|
||||
m_empty = false;
|
||||
m_hours = hours;
|
||||
}
|
||||
|
||||
void HourMinutes::SetMinutes(TMinutes const minutes)
|
||||
{
|
||||
m_empty = false;
|
||||
m_minutes = minutes;
|
||||
}
|
||||
|
||||
void HourMinutes::SetDuration(TMinutes const duration)
|
||||
{
|
||||
SetHours(std::chrono::duration_cast<THours>(duration));
|
||||
SetMinutes(duration - GetHours());
|
||||
}
|
||||
|
||||
HourMinutes operator-(HourMinutes const & hm)
|
||||
{
|
||||
HourMinutes result;
|
||||
result.SetHours(-hm.GetHours());
|
||||
result.SetMinutes(-hm.GetMinutes());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, HourMinutes const & hm)
|
||||
{
|
||||
if (hm.IsEmpty())
|
||||
ost << "hh:mm";
|
||||
else
|
||||
PrintHoursMinutes(ost, std::abs(hm.GetHoursCount()), std::abs(hm.GetMinutesCount()));
|
||||
return ost;
|
||||
}
|
||||
|
||||
// TimeEvent ---------------------------------------------------------------------------------------
|
||||
Time TimeEvent::GetEventTime() const
|
||||
{
|
||||
return Time(HourMinutes(0_h + 0_min)); // TODO(mgsergio): get real time
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, TimeEvent::Event const event)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case TimeEvent::Event::None:
|
||||
ost << "None";
|
||||
break;
|
||||
case TimeEvent::Event::Sunrise:
|
||||
ost << "sunrise";
|
||||
break;
|
||||
case TimeEvent::Event::Sunset:
|
||||
ost << "sunset";
|
||||
break;
|
||||
}
|
||||
return ost;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, TimeEvent const te)
|
||||
{
|
||||
if (te.HasOffset())
|
||||
{
|
||||
ost << '(' << te.GetEvent();
|
||||
|
||||
auto const & offset = te.GetOffset();
|
||||
|
||||
if (offset.GetHoursCount() < 0)
|
||||
ost << '-';
|
||||
else
|
||||
ost << '+';
|
||||
|
||||
ost << offset << ')';
|
||||
}
|
||||
else
|
||||
{
|
||||
ost << te.GetEvent();
|
||||
}
|
||||
|
||||
return ost;
|
||||
}
|
||||
|
||||
// Time --------------------------------------------------------------------------------------------
|
||||
Time::THours Time::GetHours() const
|
||||
{
|
||||
if (IsEvent())
|
||||
return GetEvent().GetEventTime().GetHours();
|
||||
return GetHourMinutes().GetHours();
|
||||
}
|
||||
|
||||
Time::TMinutes Time::GetMinutes() const
|
||||
{
|
||||
if (IsEvent())
|
||||
return GetEvent().GetEventTime().GetMinutes();
|
||||
return GetHourMinutes().GetMinutes();
|
||||
}
|
||||
|
||||
void Time::AddDuration(TMinutes const duration)
|
||||
{
|
||||
if (IsEvent())
|
||||
{
|
||||
m_event.AddDurationToOffset(duration);
|
||||
}
|
||||
else if (IsHoursMinutes())
|
||||
{
|
||||
m_hourMinutes.AddDuration(duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Undefined behaviour.
|
||||
}
|
||||
}
|
||||
|
||||
void Time::SetEvent(TimeEvent const & event)
|
||||
{
|
||||
m_type = Type::Event;
|
||||
m_event = event;
|
||||
}
|
||||
|
||||
void Time::SetHourMinutes(HourMinutes const & hm)
|
||||
{
|
||||
m_type = Type::HourMinutes;
|
||||
m_hourMinutes = hm;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, Time const & time)
|
||||
{
|
||||
if (time.IsEmpty())
|
||||
{
|
||||
ost << "hh:mm";
|
||||
return ost;
|
||||
}
|
||||
|
||||
if (time.IsEvent())
|
||||
ost << time.GetEvent();
|
||||
else
|
||||
ost << time.GetHourMinutes();
|
||||
|
||||
return ost;
|
||||
}
|
||||
|
||||
bool operator==(Time const & lhs, Time const & rhs)
|
||||
{
|
||||
if (lhs.IsEmpty() && rhs.IsEmpty())
|
||||
return true;
|
||||
|
||||
return lhs.GetType() == rhs.GetType() &&
|
||||
lhs.GetHours() == rhs.GetHours() &&
|
||||
lhs.GetMinutes() == rhs.GetMinutes();
|
||||
}
|
||||
|
||||
// TimespanPeriod ----------------------------------------------------------------------------------
|
||||
TimespanPeriod::TimespanPeriod(HourMinutes const & hm):
|
||||
m_hourMinutes(hm),
|
||||
m_type(Type::HourMinutes)
|
||||
{
|
||||
}
|
||||
|
||||
TimespanPeriod::TimespanPeriod(HourMinutes::TMinutes const minutes):
|
||||
m_minutes(minutes),
|
||||
m_type(Type::Minutes)
|
||||
{
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, TimespanPeriod const p)
|
||||
{
|
||||
if (p.IsEmpty())
|
||||
ost << "None";
|
||||
else if (p.IsHoursMinutes())
|
||||
ost << p.GetHourMinutes();
|
||||
else if (p.IsMinutes())
|
||||
PrintPaddedNumber(ost, p.GetMinutesCount(), 2);
|
||||
return ost;
|
||||
}
|
||||
|
||||
bool operator==(TimespanPeriod const & lhs, TimespanPeriod const & rhs)
|
||||
{
|
||||
if (lhs.IsEmpty() && rhs.IsEmpty())
|
||||
return true;
|
||||
|
||||
return lhs.GetType() == rhs.GetType() &&
|
||||
lhs.GetHourMinutes() == rhs.GetHourMinutes() &&
|
||||
lhs.GetMinutes() == rhs.GetMinutes();
|
||||
}
|
||||
|
||||
// Timespan ----------------------------------------------------------------------------------------
|
||||
bool Timespan::HasExtendedHours() const
|
||||
{
|
||||
bool const canHaveExtendedHours = HasStart() && HasEnd() &&
|
||||
GetStart().IsHoursMinutes() &&
|
||||
GetEnd().IsHoursMinutes();
|
||||
if (!canHaveExtendedHours)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const & startHM = GetStart().GetHourMinutes();
|
||||
auto const & endHM = GetEnd().GetHourMinutes();
|
||||
|
||||
if (endHM.IsExtended())
|
||||
return true;
|
||||
|
||||
return endHM.GetDuration() <= startHM.GetDuration();
|
||||
}
|
||||
|
||||
void Timespan::ExpandPlus()
|
||||
{
|
||||
if (HasPlus())
|
||||
{
|
||||
SetEnd(HourMinutes(24_h));
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, Timespan const & span)
|
||||
{
|
||||
ost << span.GetStart();
|
||||
if (!span.IsOpen())
|
||||
{
|
||||
ost << '-' << span.GetEnd();
|
||||
if (span.HasPeriod())
|
||||
ost << '/' << span.GetPeriod();
|
||||
}
|
||||
if (span.HasPlus())
|
||||
ost << '+';
|
||||
return ost;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, osmoh::TTimespans const & timespans)
|
||||
{
|
||||
PrintVector(ost, timespans);
|
||||
return ost;
|
||||
}
|
||||
|
||||
bool operator==(Timespan const & lhs, Timespan const & rhs)
|
||||
{
|
||||
if (lhs.IsEmpty() && rhs.IsEmpty())
|
||||
return true;
|
||||
|
||||
if (lhs.IsEmpty() != rhs.IsEmpty() ||
|
||||
lhs.HasStart() != rhs.HasStart() ||
|
||||
lhs.HasEnd() != rhs.HasEnd() ||
|
||||
lhs.HasPlus() != rhs.HasPlus() ||
|
||||
lhs.HasPeriod() != rhs.HasPeriod())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return lhs.GetStart() == rhs.GetStart() &&
|
||||
lhs.GetEnd() == rhs.GetEnd() &&
|
||||
lhs.GetPeriod() == rhs.GetPeriod();
|
||||
}
|
||||
|
||||
// NthWeekdayOfTheMonthEntry -----------------------------------------------------------------------
|
||||
std::ostream & operator<<(std::ostream & ost, NthWeekdayOfTheMonthEntry const entry)
|
||||
{
|
||||
if (entry.HasStart())
|
||||
ost << static_cast<uint32_t>(entry.GetStart());
|
||||
if (entry.HasEnd())
|
||||
ost << '-' << static_cast<uint32_t>(entry.GetEnd());
|
||||
return ost;
|
||||
}
|
||||
|
||||
bool NthWeekdayOfTheMonthEntry::operator==(NthWeekdayOfTheMonthEntry const & rhs) const
|
||||
{
|
||||
return m_start == rhs.m_start && m_end == rhs.m_end;
|
||||
}
|
||||
|
||||
// WeekdayRange ------------------------------------------------------------------------------------
|
||||
bool WeekdayRange::HasWday(Weekday const wday) const
|
||||
{
|
||||
if (IsEmpty() || wday == Weekday::None)
|
||||
return false;
|
||||
|
||||
if (!HasEnd())
|
||||
return GetStart() == wday;
|
||||
|
||||
return (GetStart() <= GetEnd())
|
||||
? GetStart() <= wday && wday <= GetEnd()
|
||||
: wday <= GetEnd() || GetStart() <= wday;
|
||||
}
|
||||
|
||||
bool WeekdayRange::operator==(WeekdayRange const & rhs) const
|
||||
{
|
||||
return m_start == rhs.m_start && m_end == rhs.m_end && m_offset == rhs.m_offset &&
|
||||
m_nths == rhs.m_nths;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, Weekday wday)
|
||||
{
|
||||
switch (wday)
|
||||
{
|
||||
case Weekday::Sunday:
|
||||
ost << "Su";
|
||||
break;
|
||||
case Weekday::Monday:
|
||||
ost << "Mo";
|
||||
break;
|
||||
case Weekday::Tuesday:
|
||||
ost << "Tu";
|
||||
break;
|
||||
case Weekday::Wednesday:
|
||||
ost << "We";
|
||||
break;
|
||||
case Weekday::Thursday:
|
||||
ost << "Th";
|
||||
break;
|
||||
case Weekday::Friday:
|
||||
ost << "Fr";
|
||||
break;
|
||||
case Weekday::Saturday:
|
||||
ost << "Sa";
|
||||
break;
|
||||
case Weekday::None:
|
||||
ost << "None";
|
||||
}
|
||||
return ost;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, WeekdayRange const & range)
|
||||
{
|
||||
ost << range.GetStart();
|
||||
if (range.HasEnd())
|
||||
{
|
||||
ost << '-' << range.GetEnd();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (range.HasNth())
|
||||
{
|
||||
ost << '[';
|
||||
PrintVector(ost, range.GetNths(), ",");
|
||||
ost << ']';
|
||||
}
|
||||
PrintOffset(ost, range.GetOffset(), true);
|
||||
}
|
||||
return ost;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, TWeekdayRanges const & ranges)
|
||||
{
|
||||
PrintVector(ost, ranges);
|
||||
return ost;
|
||||
}
|
||||
|
||||
// Holiday -----------------------------------------------------------------------------------------
|
||||
std::ostream & operator<<(std::ostream & ost, Holiday const & holiday)
|
||||
{
|
||||
if (holiday.IsPlural())
|
||||
{
|
||||
ost << "PH";
|
||||
}
|
||||
else
|
||||
{
|
||||
ost << "SH";
|
||||
PrintOffset(ost, holiday.GetOffset(), true);
|
||||
}
|
||||
return ost;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, THolidays const & holidays)
|
||||
{
|
||||
PrintVector(ost, holidays);
|
||||
return ost;
|
||||
}
|
||||
|
||||
bool Holiday::operator==(Holiday const & rhs) const
|
||||
{
|
||||
return m_plural == rhs.m_plural && m_offset == rhs.m_offset;
|
||||
}
|
||||
|
||||
// Weekdays ----------------------------------------------------------------------------------------
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, Weekdays const & weekday)
|
||||
{
|
||||
ost << weekday.GetHolidays();
|
||||
if (weekday.HasWeekday() && weekday.HasHolidays())
|
||||
ost << ", ";
|
||||
ost << weekday.GetWeekdayRanges();
|
||||
return ost;
|
||||
}
|
||||
|
||||
bool Weekdays::operator==(Weekdays const & rhs) const
|
||||
{
|
||||
return m_weekdayRanges == rhs.m_weekdayRanges && m_holidays == rhs.m_holidays;
|
||||
}
|
||||
|
||||
// DateOffset --------------------------------------------------------------------------------------
|
||||
std::ostream & operator<<(std::ostream & ost, DateOffset const & offset)
|
||||
{
|
||||
if (offset.HasWDayOffset())
|
||||
{
|
||||
ost << (offset.IsWDayOffsetPositive() ? '+' : '-')
|
||||
<< offset.GetWDayOffset();
|
||||
}
|
||||
PrintOffset(ost, offset.GetOffset(), offset.HasWDayOffset());
|
||||
return ost;
|
||||
}
|
||||
|
||||
bool DateOffset::operator==(DateOffset const & rhs) const
|
||||
{
|
||||
return m_wdayOffest == rhs.m_wdayOffest && m_positive == rhs.m_positive &&
|
||||
m_offset == rhs.m_offset;
|
||||
}
|
||||
|
||||
bool DateOffset::operator<(DateOffset const & rhs) const
|
||||
{
|
||||
return std::tie(m_wdayOffest, m_positive, m_offset) <
|
||||
std::tie(rhs.m_wdayOffest, rhs.m_positive, rhs.m_offset);
|
||||
}
|
||||
|
||||
// MonthDay ----------------------------------------------------------------------------------------
|
||||
std::ostream & operator<<(std::ostream & ost, MonthDay::Month const month)
|
||||
{
|
||||
switch (month)
|
||||
{
|
||||
case MonthDay::Month::None:
|
||||
ost << "None";
|
||||
break;
|
||||
case MonthDay::Month::Jan:
|
||||
ost << "Jan";
|
||||
break;
|
||||
case MonthDay::Month::Feb:
|
||||
ost << "Feb";
|
||||
break;
|
||||
case MonthDay::Month::Mar:
|
||||
ost << "Mar";
|
||||
break;
|
||||
case MonthDay::Month::Apr:
|
||||
ost << "Apr";
|
||||
break;
|
||||
case MonthDay::Month::May:
|
||||
ost << "May";
|
||||
break;
|
||||
case MonthDay::Month::Jun:
|
||||
ost << "Jun";
|
||||
break;
|
||||
case MonthDay::Month::Jul:
|
||||
ost << "Jul";
|
||||
break;
|
||||
case MonthDay::Month::Aug:
|
||||
ost << "Aug";
|
||||
break;
|
||||
case MonthDay::Month::Sep:
|
||||
ost << "Sep";
|
||||
break;
|
||||
case MonthDay::Month::Oct:
|
||||
ost << "Oct";
|
||||
break;
|
||||
case MonthDay::Month::Nov:
|
||||
ost << "Nov";
|
||||
break;
|
||||
case MonthDay::Month::Dec:
|
||||
ost << "Dec";
|
||||
break;
|
||||
}
|
||||
return ost;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, MonthDay::VariableDate const date)
|
||||
{
|
||||
switch (date)
|
||||
{
|
||||
case MonthDay::VariableDate::None:
|
||||
ost << "none";
|
||||
break;
|
||||
case MonthDay::VariableDate::Easter:
|
||||
ost << "easter";
|
||||
break;
|
||||
}
|
||||
return ost;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, MonthDay const md)
|
||||
{
|
||||
bool space = false;
|
||||
auto const putSpace = [&space, &ost] {
|
||||
if (space)
|
||||
ost << ' ';
|
||||
space = true;
|
||||
};
|
||||
|
||||
if (md.HasYear())
|
||||
{
|
||||
putSpace();
|
||||
ost << md.GetYear();
|
||||
}
|
||||
|
||||
if (md.IsVariable())
|
||||
{
|
||||
putSpace();
|
||||
ost << md.GetVariableDate();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (md.HasMonth())
|
||||
{
|
||||
putSpace();
|
||||
ost << md.GetMonth();
|
||||
}
|
||||
if (md.HasDayNum())
|
||||
{
|
||||
putSpace();
|
||||
PrintPaddedNumber(ost, md.GetDayNum(), 2);
|
||||
}
|
||||
}
|
||||
if (md.HasOffset())
|
||||
{
|
||||
ost << ' ' << md.GetOffset();
|
||||
}
|
||||
return ost;
|
||||
}
|
||||
|
||||
bool MonthDay::operator==(MonthDay const & rhs) const
|
||||
{
|
||||
return m_year == rhs.m_year && m_month == rhs.m_month && m_daynum == rhs.m_daynum &&
|
||||
m_variable_date == rhs.m_variable_date && m_offset == rhs.m_offset;
|
||||
}
|
||||
|
||||
bool MonthDay::operator<(MonthDay const & rhs) const
|
||||
{
|
||||
return std::tie(m_year, m_month, m_daynum, m_variable_date, m_offset) <
|
||||
std::tie(rhs.m_year, rhs.m_month, rhs.m_daynum, rhs.m_variable_date, rhs.m_offset);
|
||||
}
|
||||
|
||||
// MonthdayRange -----------------------------------------------------------------------------------
|
||||
std::ostream & operator<<(std::ostream & ost, MonthdayRange const & range)
|
||||
{
|
||||
if (range.HasStart())
|
||||
ost << range.GetStart();
|
||||
if (range.HasEnd())
|
||||
{
|
||||
ost << '-' << range.GetEnd();
|
||||
if (range.HasPeriod())
|
||||
ost << '/' << range.GetPeriod();
|
||||
}
|
||||
else if (range.HasPlus())
|
||||
ost << '+';
|
||||
return ost;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, TMonthdayRanges const & ranges)
|
||||
{
|
||||
PrintVector(ost, ranges);
|
||||
return ost;
|
||||
}
|
||||
|
||||
bool MonthdayRange::operator==(MonthdayRange const & rhs) const
|
||||
{
|
||||
return m_start == rhs.m_start && m_end == rhs.m_end && m_period == rhs.m_period &&
|
||||
m_plus == rhs.m_plus;
|
||||
}
|
||||
|
||||
// YearRange ---------------------------------------------------------------------------------------
|
||||
std::ostream & operator<<(std::ostream & ost, YearRange const range)
|
||||
{
|
||||
if (range.IsEmpty())
|
||||
return ost;
|
||||
|
||||
ost << range.GetStart();
|
||||
if (range.HasEnd())
|
||||
{
|
||||
ost << '-' << range.GetEnd();
|
||||
if (range.HasPeriod())
|
||||
ost << '/' << range.GetPeriod();
|
||||
}
|
||||
else if (range.HasPlus())
|
||||
{
|
||||
ost << '+';
|
||||
}
|
||||
|
||||
return ost;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, TYearRanges const ranges)
|
||||
{
|
||||
PrintVector(ost, ranges);
|
||||
return ost;
|
||||
}
|
||||
|
||||
bool YearRange::operator==(YearRange const & rhs) const
|
||||
{
|
||||
return m_start == rhs.m_start && m_end == rhs.m_end && m_plus == rhs.m_plus &&
|
||||
m_period == rhs.m_period;
|
||||
}
|
||||
|
||||
// WeekRange ---------------------------------------------------------------------------------------
|
||||
std::ostream & operator<<(std::ostream & ost, WeekRange const range)
|
||||
{
|
||||
if (range.IsEmpty())
|
||||
return ost;
|
||||
|
||||
PrintPaddedNumber(ost, range.GetStart(), 2);
|
||||
if (range.HasEnd())
|
||||
{
|
||||
ost << '-';
|
||||
PrintPaddedNumber(ost, range.GetEnd(), 2);
|
||||
if (range.HasPeriod())
|
||||
ost << '/' << range.GetPeriod();
|
||||
}
|
||||
return ost;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, TWeekRanges const ranges)
|
||||
{
|
||||
ost << "week ";
|
||||
PrintVector(ost, ranges);
|
||||
return ost;
|
||||
}
|
||||
|
||||
bool WeekRange::operator==(WeekRange const & rhs) const
|
||||
{
|
||||
return m_start == rhs.m_start && m_end == rhs.m_end && m_period == rhs.m_period;
|
||||
}
|
||||
|
||||
// RuleSequence ------------------------------------------------------------------------------------
|
||||
bool RuleSequence::HasMonthDay() const
|
||||
{
|
||||
for (auto const & monthRange : GetMonths())
|
||||
{
|
||||
if (monthRange.GetStart().GetDayNum())
|
||||
return true;
|
||||
|
||||
if (monthRange.GetEnd().GetDayNum())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RuleSequence::operator==(RuleSequence const & rhs) const
|
||||
{
|
||||
return m_twentyFourHours == rhs.m_twentyFourHours && m_years == rhs.m_years &&
|
||||
m_months == rhs.m_months && m_weeks == rhs.m_weeks && m_weekdays == rhs.m_weekdays &&
|
||||
m_times == rhs.m_times && m_comment == rhs.m_comment &&
|
||||
m_anySeparator == rhs.m_anySeparator &&
|
||||
m_separatorForReadability == rhs.m_separatorForReadability &&
|
||||
m_modifier == rhs.m_modifier && m_modifierComment == rhs.m_modifierComment;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, RuleSequence::Modifier const modifier)
|
||||
{
|
||||
switch (modifier)
|
||||
{
|
||||
case RuleSequence::Modifier::DefaultOpen:
|
||||
case RuleSequence::Modifier::Comment:
|
||||
break;
|
||||
case RuleSequence::Modifier::Unknown:
|
||||
ost << "unknown";
|
||||
break;
|
||||
case RuleSequence::Modifier::Closed:
|
||||
ost << "closed";
|
||||
break;
|
||||
case RuleSequence::Modifier::Open:
|
||||
ost << "open";
|
||||
break;
|
||||
}
|
||||
return ost;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, RuleSequence const & s)
|
||||
{
|
||||
bool space = false;
|
||||
auto const putSpace = [&space, &ost] {
|
||||
if (space)
|
||||
ost << ' ';
|
||||
space = true;
|
||||
};
|
||||
|
||||
if (s.IsTwentyFourHours())
|
||||
{
|
||||
putSpace();
|
||||
ost << "24/7";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s.HasComment())
|
||||
ost << s.GetComment() << ':';
|
||||
else
|
||||
{
|
||||
if (s.HasYears())
|
||||
{
|
||||
putSpace();
|
||||
ost << s.GetYears();
|
||||
}
|
||||
if (s.HasMonths())
|
||||
{
|
||||
putSpace();
|
||||
ost << s.GetMonths();
|
||||
}
|
||||
if (s.HasWeeks())
|
||||
{
|
||||
putSpace();
|
||||
ost << s.GetWeeks();
|
||||
}
|
||||
|
||||
if (s.HasSeparatorForReadability())
|
||||
ost << ':';
|
||||
|
||||
if (s.HasWeekdays())
|
||||
{
|
||||
putSpace();
|
||||
ost << s.GetWeekdays();
|
||||
}
|
||||
if (s.HasTimes())
|
||||
{
|
||||
putSpace();
|
||||
ost << s.GetTimes();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.GetModifier() != RuleSequence::Modifier::DefaultOpen &&
|
||||
s.GetModifier() != RuleSequence::Modifier::Comment)
|
||||
{
|
||||
putSpace();
|
||||
ost << s.GetModifier();
|
||||
}
|
||||
if (s.HasModifierComment())
|
||||
{
|
||||
putSpace();
|
||||
ost << '"' << s.GetModifierComment() << '"';
|
||||
}
|
||||
|
||||
return ost;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, TRuleSequences const & s)
|
||||
{
|
||||
PrintVector(ost, s, [](RuleSequence const & r) {
|
||||
auto const sep = r.GetAnySeparator();
|
||||
return (sep == "||" ? ' ' + sep + ' ' : sep + ' ');
|
||||
});
|
||||
return ost;
|
||||
}
|
||||
|
||||
// OpeningHours ------------------------------------------------------------------------------------
|
||||
OpeningHours::OpeningHours(std::string const & rule):
|
||||
m_valid(Parse(rule, m_rule))
|
||||
{
|
||||
}
|
||||
|
||||
OpeningHours::OpeningHours(TRuleSequences const & rule):
|
||||
m_rule(rule),
|
||||
m_valid(true)
|
||||
{
|
||||
}
|
||||
|
||||
bool OpeningHours::IsOpen(time_t const dateTime) const
|
||||
{
|
||||
return osmoh::IsOpen(m_rule, dateTime);
|
||||
}
|
||||
|
||||
bool OpeningHours::IsClosed(time_t const dateTime) const
|
||||
{
|
||||
return osmoh::IsClosed(m_rule, dateTime);
|
||||
}
|
||||
|
||||
bool OpeningHours::IsUnknown(time_t const dateTime) const
|
||||
{
|
||||
return osmoh::IsUnknown(m_rule, dateTime);
|
||||
}
|
||||
|
||||
OpeningHours::InfoT OpeningHours::GetInfo(time_t const dateTime) const
|
||||
{
|
||||
InfoT info;
|
||||
info.state = GetState(m_rule, dateTime);
|
||||
if (info.state != RuleState::Unknown)
|
||||
{
|
||||
if (info.state == RuleState::Open)
|
||||
info.nextTimeOpen = dateTime;
|
||||
else
|
||||
info.nextTimeOpen = osmoh::GetNextTimeState(m_rule, dateTime, RuleState::Open);
|
||||
|
||||
if (info.state == RuleState::Closed)
|
||||
info.nextTimeClosed = dateTime;
|
||||
else
|
||||
info.nextTimeClosed = osmoh::GetNextTimeState(m_rule, dateTime, RuleState::Closed);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
bool OpeningHours::IsValid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
bool OpeningHours::IsTwentyFourHours() const
|
||||
{
|
||||
return m_rule.size() == 1 && m_rule[0].IsTwentyFourHours();
|
||||
}
|
||||
|
||||
bool OpeningHours::HasWeekdaySelector() const
|
||||
{
|
||||
return std::any_of(m_rule.cbegin(), m_rule.cend(), std::mem_fn(&osmoh::RuleSequence::HasWeekdays));
|
||||
}
|
||||
|
||||
bool OpeningHours::HasMonthSelector() const
|
||||
{
|
||||
return std::any_of(m_rule.cbegin(), m_rule.cend(), std::mem_fn(&osmoh::RuleSequence::HasMonths));
|
||||
}
|
||||
|
||||
bool OpeningHours::HasWeekSelector() const
|
||||
{
|
||||
return std::any_of(m_rule.cbegin(), m_rule.cend(), std::mem_fn(&osmoh::RuleSequence::HasWeeks));
|
||||
}
|
||||
|
||||
bool OpeningHours::HasYearSelector() const
|
||||
{
|
||||
return std::any_of(m_rule.cbegin(), m_rule.cend(), std::mem_fn(&osmoh::RuleSequence::HasYears));
|
||||
}
|
||||
|
||||
void swap(OpeningHours & lhs, OpeningHours & rhs)
|
||||
{
|
||||
std::swap(lhs.m_rule, rhs.m_rule);
|
||||
std::swap(lhs.m_valid, rhs.m_valid);
|
||||
}
|
||||
|
||||
bool OpeningHours::operator==(OpeningHours const & rhs) const
|
||||
{
|
||||
return m_valid == rhs.m_valid && m_rule == rhs.m_rule;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, OpeningHours const & oh)
|
||||
{
|
||||
ost << oh.GetRule();
|
||||
return ost;
|
||||
}
|
||||
|
||||
std::string ToString(osmoh::OpeningHours const & openingHours)
|
||||
{
|
||||
if (!openingHours.IsValid())
|
||||
return {};
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << openingHours;
|
||||
return stream.str();
|
||||
}
|
||||
} // namespace osmoh
|
||||
745
3party/opening_hours/opening_hours.hpp
Normal file
745
3party/opening_hours/opening_hours.hpp
Normal file
|
|
@ -0,0 +1,745 @@
|
|||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Mail.Ru Group
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
// Implemented in accordance with the specification
|
||||
// https://wiki.openstreetmap.org/wiki/Key:opening_hours/specification
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
class HourMinutes
|
||||
{
|
||||
public:
|
||||
using THours = std::chrono::hours;
|
||||
using TMinutes = std::chrono::minutes;
|
||||
|
||||
HourMinutes() = default;
|
||||
HourMinutes(THours const duration) { SetDuration(duration); }
|
||||
HourMinutes(TMinutes const duration) { SetDuration(duration); }
|
||||
|
||||
bool IsEmpty() const { return m_empty; }
|
||||
bool IsExtended() const;
|
||||
|
||||
THours GetHours() const { return m_hours; }
|
||||
TMinutes GetMinutes() const { return m_minutes; }
|
||||
TMinutes GetDuration() const { return GetMinutes() + GetHours(); }
|
||||
|
||||
THours::rep GetHoursCount() const { return GetHours().count(); }
|
||||
TMinutes::rep GetMinutesCount() const { return GetMinutes().count(); }
|
||||
TMinutes::rep GetDurationCount() const { return GetDuration().count(); }
|
||||
|
||||
void SetHours(THours const hours);
|
||||
void SetMinutes(TMinutes const minutes);
|
||||
void SetDuration(TMinutes const duration);
|
||||
|
||||
void AddDuration(TMinutes const duration) { SetDuration(GetDuration() + duration); }
|
||||
|
||||
private:
|
||||
THours m_hours = THours::zero();
|
||||
TMinutes m_minutes = TMinutes::zero();
|
||||
bool m_empty = true;
|
||||
};
|
||||
|
||||
HourMinutes operator-(HourMinutes const & hm);
|
||||
std::ostream & operator<<(std::ostream & ost, HourMinutes const & hm);
|
||||
|
||||
inline bool operator<(HourMinutes const & a, HourMinutes const & b)
|
||||
{
|
||||
return a.GetDuration() < b.GetDuration();
|
||||
}
|
||||
|
||||
inline bool operator==(HourMinutes const & a, HourMinutes const & b)
|
||||
{
|
||||
return a.GetDuration() == b.GetDuration();
|
||||
}
|
||||
|
||||
class Time;
|
||||
|
||||
class TimeEvent
|
||||
{
|
||||
public:
|
||||
enum class Event
|
||||
{
|
||||
None,
|
||||
Sunrise,
|
||||
Sunset
|
||||
};
|
||||
|
||||
TimeEvent() = default;
|
||||
TimeEvent(Event const event): m_event(event) {}
|
||||
|
||||
bool IsEmpty() const { return m_event == Event::None; }
|
||||
bool HasOffset() const { return !m_offset.IsEmpty(); }
|
||||
|
||||
Event GetEvent() const { return m_event; }
|
||||
void SetEvent(Event const event) { m_event = event; }
|
||||
|
||||
HourMinutes const & GetOffset() const { return m_offset; }
|
||||
void SetOffset(HourMinutes const & offset) { m_offset = offset; }
|
||||
void AddDurationToOffset(HourMinutes::TMinutes const duration) { m_offset.AddDuration(duration); }
|
||||
|
||||
Time GetEventTime() const;
|
||||
|
||||
private:
|
||||
Event m_event = Event::None;
|
||||
HourMinutes m_offset;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, TimeEvent const te);
|
||||
|
||||
class Time
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
None,
|
||||
HourMinutes,
|
||||
Event,
|
||||
};
|
||||
|
||||
public:
|
||||
using THours = HourMinutes::THours;
|
||||
using TMinutes = HourMinutes::TMinutes;
|
||||
|
||||
Time() = default;
|
||||
Time(HourMinutes const & hm) { SetHourMinutes(hm); }
|
||||
Time(TimeEvent const & te) { SetEvent(te); }
|
||||
|
||||
bool IsEmpty() const { return GetType() == Type::None; }
|
||||
bool IsTime() const { return IsHoursMinutes() || IsEvent(); }
|
||||
bool IsEvent() const { return GetType() == Type::Event; }
|
||||
bool IsHoursMinutes() const { return GetType() == Type::HourMinutes; }
|
||||
|
||||
Type GetType() const { return m_type; }
|
||||
|
||||
THours::rep GetHoursCount() const { return GetHours().count(); }
|
||||
TMinutes::rep GetMinutesCount() const { return GetMinutes().count(); }
|
||||
|
||||
THours GetHours() const;
|
||||
TMinutes GetMinutes() const;
|
||||
|
||||
void AddDuration(TMinutes const duration);
|
||||
|
||||
TimeEvent const & GetEvent() const { return m_event; }
|
||||
void SetEvent(TimeEvent const & event);
|
||||
|
||||
HourMinutes const & GetHourMinutes() const { return m_hourMinutes; }
|
||||
HourMinutes & GetHourMinutes() { return m_hourMinutes; }
|
||||
void SetHourMinutes(HourMinutes const & hm);
|
||||
|
||||
private:
|
||||
HourMinutes m_hourMinutes;
|
||||
TimeEvent m_event;
|
||||
|
||||
Type m_type = Type::None;
|
||||
};
|
||||
|
||||
inline constexpr Time::THours operator ""_h(unsigned long long int h)
|
||||
{
|
||||
return Time::THours(h);
|
||||
}
|
||||
|
||||
inline constexpr Time::TMinutes operator ""_min(unsigned long long int m)
|
||||
{
|
||||
return Time::TMinutes(m);
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, Time const & time);
|
||||
bool operator==(Time const & lhs, Time const & rhs);
|
||||
|
||||
class TimespanPeriod
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
None,
|
||||
Minutes,
|
||||
HourMinutes
|
||||
};
|
||||
|
||||
TimespanPeriod() = default;
|
||||
TimespanPeriod(HourMinutes const & hm);
|
||||
TimespanPeriod(HourMinutes::TMinutes const minutes);
|
||||
|
||||
bool IsEmpty() const { return m_type == Type::None; }
|
||||
bool IsHoursMinutes() const { return m_type == Type::HourMinutes; }
|
||||
bool IsMinutes() const { return m_type == Type::Minutes; }
|
||||
|
||||
Type GetType() const { return m_type; }
|
||||
|
||||
HourMinutes const & GetHourMinutes() const { return m_hourMinutes; }
|
||||
HourMinutes::TMinutes GetMinutes() const { return m_minutes; }
|
||||
HourMinutes::TMinutes::rep GetMinutesCount() const { return GetMinutes().count(); }
|
||||
|
||||
private:
|
||||
HourMinutes::TMinutes m_minutes;
|
||||
HourMinutes m_hourMinutes;
|
||||
|
||||
Type m_type = Type::None;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, TimespanPeriod const p);
|
||||
bool operator==(TimespanPeriod const & lhs, TimespanPeriod const & rhs);
|
||||
|
||||
class Timespan
|
||||
{
|
||||
public:
|
||||
Timespan() = default;
|
||||
Timespan(Time const & start, Time const & end): m_start(start), m_end(end) {}
|
||||
Timespan(HourMinutes::TMinutes const & start,
|
||||
HourMinutes::TMinutes const & end): m_start(start), m_end(end) {}
|
||||
|
||||
bool IsEmpty() const { return !HasStart() && !HasEnd(); }
|
||||
bool IsOpen() const { return HasStart() && !HasEnd(); }
|
||||
bool HasStart() const { return !GetStart().IsEmpty(); }
|
||||
bool HasEnd() const { return !GetEnd().IsEmpty(); }
|
||||
bool HasPlus() const { return m_plus; }
|
||||
bool HasPeriod() const { return !m_period.IsEmpty(); }
|
||||
bool HasExtendedHours() const;
|
||||
|
||||
Time const & GetStart() const { return m_start; }
|
||||
Time const & GetEnd() const { return m_end; }
|
||||
|
||||
Time & GetStart() { return m_start; }
|
||||
Time & GetEnd() { return m_end; }
|
||||
|
||||
TimespanPeriod const & GetPeriod() const { return m_period; }
|
||||
|
||||
void SetStart(Time const & start) { m_start = start; }
|
||||
void SetEnd(Time const & end) { m_end = end; }
|
||||
void SetPeriod(TimespanPeriod const & period) { m_period = period; }
|
||||
void SetPlus(bool const plus) { m_plus = plus; }
|
||||
|
||||
void ExpandPlus();
|
||||
|
||||
private:
|
||||
Time m_start;
|
||||
Time m_end;
|
||||
TimespanPeriod m_period;
|
||||
bool m_plus = false;
|
||||
};
|
||||
|
||||
using TTimespans = std::vector<Timespan>;
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, Timespan const & span);
|
||||
std::ostream & operator<<(std::ostream & ost, osmoh::TTimespans const & timespans);
|
||||
bool operator==(Timespan const & lhs, Timespan const & rhs);
|
||||
|
||||
class NthWeekdayOfTheMonthEntry
|
||||
{
|
||||
public:
|
||||
enum class NthDayOfTheMonth
|
||||
{
|
||||
None,
|
||||
First,
|
||||
Second,
|
||||
Third,
|
||||
Fourth,
|
||||
Fifth
|
||||
};
|
||||
|
||||
bool IsEmpty() const { return !HasStart() && !HasEnd(); }
|
||||
bool HasStart() const { return GetStart() != NthDayOfTheMonth::None; }
|
||||
bool HasEnd() const { return GetEnd() != NthDayOfTheMonth::None; }
|
||||
|
||||
NthDayOfTheMonth GetStart() const { return m_start; }
|
||||
NthDayOfTheMonth GetEnd() const { return m_end; }
|
||||
|
||||
void SetStart(NthDayOfTheMonth const s) { m_start = s; }
|
||||
void SetEnd(NthDayOfTheMonth const e) { m_end = e; }
|
||||
|
||||
bool operator==(NthWeekdayOfTheMonthEntry const & rhs) const;
|
||||
|
||||
private:
|
||||
NthDayOfTheMonth m_start = NthDayOfTheMonth::None;
|
||||
NthDayOfTheMonth m_end = NthDayOfTheMonth::None;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, NthWeekdayOfTheMonthEntry const entry);
|
||||
|
||||
enum class Weekday
|
||||
{
|
||||
None,
|
||||
Sunday,
|
||||
Monday,
|
||||
Tuesday,
|
||||
Wednesday,
|
||||
Thursday,
|
||||
Friday,
|
||||
Saturday
|
||||
};
|
||||
|
||||
inline constexpr Weekday ToWeekday(uint64_t day)
|
||||
{
|
||||
using TDay = decltype(day);
|
||||
return ((day <= static_cast<TDay>(Weekday::None) ||
|
||||
day > static_cast<TDay>(Weekday::Saturday))
|
||||
? Weekday::None
|
||||
: static_cast<Weekday>(day));
|
||||
}
|
||||
|
||||
inline constexpr Weekday operator ""_weekday(unsigned long long int day)
|
||||
{
|
||||
return ToWeekday(day);
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, Weekday wday);
|
||||
|
||||
class WeekdayRange
|
||||
{
|
||||
using TNths = std::vector<NthWeekdayOfTheMonthEntry>;
|
||||
|
||||
public:
|
||||
bool HasWday(Weekday const wday) const;
|
||||
|
||||
bool HasSunday() const { return HasWday(Weekday::Sunday); }
|
||||
bool HasMonday() const { return HasWday(Weekday::Monday); }
|
||||
bool HasTuesday() const { return HasWday(Weekday::Tuesday); }
|
||||
bool HasWednesday() const { return HasWday(Weekday::Wednesday); }
|
||||
bool HasThursday() const { return HasWday(Weekday::Thursday); }
|
||||
bool HasFriday() const { return HasWday(Weekday::Friday); }
|
||||
bool HasSaturday() const { return HasWday(Weekday::Saturday); }
|
||||
|
||||
bool HasStart() const { return GetStart() != Weekday::None; }
|
||||
bool HasEnd() const {return GetEnd() != Weekday::None; }
|
||||
bool HasOffset() const { return GetOffset() != 0; }
|
||||
bool IsEmpty() const { return GetStart() == Weekday::None &&
|
||||
GetEnd() == Weekday::None; }
|
||||
|
||||
Weekday GetStart() const { return m_start; }
|
||||
Weekday GetEnd() const { return m_end; }
|
||||
|
||||
void SetStart(Weekday const & wday) { m_start = wday; }
|
||||
void SetEnd(Weekday const & wday) { m_end = wday; }
|
||||
|
||||
int32_t GetOffset() const { return m_offset; }
|
||||
void SetOffset(int32_t const offset) { m_offset = offset; }
|
||||
|
||||
bool HasNth() const { return !m_nths.empty(); }
|
||||
TNths const & GetNths() const { return m_nths; }
|
||||
|
||||
void AddNth(NthWeekdayOfTheMonthEntry const & entry) { m_nths.push_back(entry); }
|
||||
|
||||
bool operator==(WeekdayRange const & rhs) const;
|
||||
|
||||
private:
|
||||
Weekday m_start = Weekday::None;
|
||||
Weekday m_end = Weekday::None;
|
||||
int32_t m_offset = 0;
|
||||
TNths m_nths;
|
||||
};
|
||||
|
||||
using TWeekdayRanges = std::vector<WeekdayRange>;
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, WeekdayRange const & range);
|
||||
std::ostream & operator<<(std::ostream & ost, TWeekdayRanges const & ranges);
|
||||
|
||||
class Holiday
|
||||
{
|
||||
public:
|
||||
bool IsPlural() const { return m_plural; }
|
||||
void SetPlural(bool const plural) { m_plural = plural; }
|
||||
|
||||
int32_t GetOffset() const { return m_offset; }
|
||||
void SetOffset(int32_t const offset) { m_offset = offset; }
|
||||
|
||||
bool operator==(Holiday const & rhs) const;
|
||||
|
||||
private:
|
||||
bool m_plural = false;
|
||||
int32_t m_offset = 0;
|
||||
};
|
||||
|
||||
using THolidays = std::vector<Holiday>;
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, Holiday const & holiday);
|
||||
std::ostream & operator<<(std::ostream & ost, THolidays const & holidys);
|
||||
|
||||
// Correspond to weekday_selector in osm opening hours.
|
||||
class Weekdays
|
||||
{
|
||||
public:
|
||||
bool IsEmpty() const { return GetWeekdayRanges().empty() && GetHolidays().empty(); }
|
||||
bool HasWeekday() const { return !GetWeekdayRanges().empty(); }
|
||||
bool HasHolidays() const { return !GetHolidays().empty(); }
|
||||
|
||||
TWeekdayRanges const & GetWeekdayRanges() const { return m_weekdayRanges; }
|
||||
THolidays const & GetHolidays() const { return m_holidays; }
|
||||
|
||||
void SetWeekdayRanges(TWeekdayRanges const ranges) { m_weekdayRanges = ranges; }
|
||||
void SetHolidays(THolidays const & holidays) { m_holidays = holidays; }
|
||||
|
||||
void AddWeekdayRange(WeekdayRange const range) { m_weekdayRanges.push_back(range); }
|
||||
void AddHoliday(Holiday const & holiday) { m_holidays.push_back(holiday); }
|
||||
|
||||
bool operator==(Weekdays const & rhs) const;
|
||||
|
||||
private:
|
||||
TWeekdayRanges m_weekdayRanges;
|
||||
THolidays m_holidays;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, Weekdays const & weekday);
|
||||
|
||||
class DateOffset
|
||||
{
|
||||
public:
|
||||
bool IsEmpty() const { return !HasOffset() && !HasWDayOffset(); }
|
||||
bool HasWDayOffset() const { return m_wdayOffest != Weekday::None; }
|
||||
bool HasOffset() const { return m_offset != 0; }
|
||||
|
||||
bool IsWDayOffsetPositive() const { return m_positive; }
|
||||
|
||||
Weekday GetWDayOffset() const { return m_wdayOffest; }
|
||||
int32_t GetOffset() const { return m_offset; }
|
||||
|
||||
void SetWDayOffset(Weekday const wday) { m_wdayOffest = wday; }
|
||||
void SetOffset(int32_t const offset) { m_offset = offset; }
|
||||
void SetWDayOffsetPositive(bool const on) { m_positive = on; }
|
||||
|
||||
bool operator==(DateOffset const & rhs) const;
|
||||
bool operator!=(DateOffset const & rhs) const { return !(*this == rhs); }
|
||||
bool operator<(DateOffset const & rhs) const;
|
||||
|
||||
private:
|
||||
Weekday m_wdayOffest = Weekday::None;
|
||||
bool m_positive = true;
|
||||
int32_t m_offset = 0;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, DateOffset const & offset);
|
||||
|
||||
class MonthDay
|
||||
{
|
||||
public:
|
||||
enum class Month
|
||||
{
|
||||
None,
|
||||
Jan,
|
||||
Feb,
|
||||
Mar,
|
||||
Apr,
|
||||
May,
|
||||
Jun,
|
||||
Jul,
|
||||
Aug,
|
||||
Sep,
|
||||
Oct,
|
||||
Nov,
|
||||
Dec
|
||||
};
|
||||
|
||||
enum class VariableDate
|
||||
{
|
||||
None,
|
||||
Easter
|
||||
};
|
||||
|
||||
using TYear = uint16_t;
|
||||
using TDayNum = uint8_t;
|
||||
|
||||
bool IsEmpty() const { return !HasYear() && !HasMonth() && !HasDayNum() && !IsVariable(); }
|
||||
bool IsVariable() const { return GetVariableDate() != VariableDate::None; }
|
||||
|
||||
bool HasYear() const { return GetYear() != 0; }
|
||||
bool HasMonth() const { return GetMonth() != Month::None; }
|
||||
bool HasDayNum() const { return GetDayNum() != 0; }
|
||||
bool HasOffset() const { return !GetOffset().IsEmpty(); }
|
||||
|
||||
TYear GetYear() const { return m_year; }
|
||||
Month GetMonth() const { return m_month; }
|
||||
TDayNum GetDayNum() const { return m_daynum; }
|
||||
DateOffset const & GetOffset() const { return m_offset; }
|
||||
VariableDate GetVariableDate() const { return m_variable_date; }
|
||||
|
||||
void SetYear(TYear const year) { m_year = year; }
|
||||
void SetMonth(Month const month) { m_month = month; }
|
||||
void SetDayNum(TDayNum const daynum) { m_daynum = daynum; }
|
||||
void SetOffset(DateOffset const & offset) { m_offset = offset; }
|
||||
void SetVariableDate(VariableDate const date) { m_variable_date = date; }
|
||||
|
||||
bool operator==(MonthDay const & rhs) const;
|
||||
bool operator<(MonthDay const & rhs) const;
|
||||
bool operator<=(MonthDay const & rhs) const { return *this < rhs || *this == rhs; }
|
||||
|
||||
private:
|
||||
TYear m_year = 0;
|
||||
Month m_month = Month::None;
|
||||
TDayNum m_daynum = 0;
|
||||
VariableDate m_variable_date = VariableDate::None;
|
||||
DateOffset m_offset;
|
||||
};
|
||||
|
||||
inline constexpr MonthDay::Month ToMonth(uint64_t month)
|
||||
{
|
||||
using TMonth = decltype(month);
|
||||
return ((month <= static_cast<TMonth>(MonthDay::Month::None) ||
|
||||
month > static_cast<TMonth>(MonthDay::Month::Dec))
|
||||
? MonthDay::Month::None
|
||||
: static_cast<osmoh::MonthDay::Month>(month));
|
||||
}
|
||||
|
||||
inline constexpr MonthDay::Month operator ""_M(unsigned long long int month)
|
||||
{
|
||||
return ToMonth(month);
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, MonthDay::Month const month);
|
||||
std::ostream & operator<<(std::ostream & ost, MonthDay::VariableDate const date);
|
||||
std::ostream & operator<<(std::ostream & ost, MonthDay const md);
|
||||
|
||||
class MonthdayRange
|
||||
{
|
||||
public:
|
||||
bool IsEmpty() const { return !HasStart() && !HasEnd(); }
|
||||
bool HasStart() const { return !GetStart().IsEmpty(); }
|
||||
bool HasEnd() const { return !GetEnd().IsEmpty() || GetEnd().HasDayNum(); }
|
||||
bool HasPeriod() const { return m_period != 0; }
|
||||
bool HasPlus() const { return m_plus; }
|
||||
|
||||
MonthDay const & GetStart() const { return m_start; }
|
||||
MonthDay const & GetEnd() const { return m_end; }
|
||||
uint32_t GetPeriod() const { return m_period; }
|
||||
|
||||
void SetStart(MonthDay const & start) { m_start = start; }
|
||||
void SetEnd(MonthDay const & end) { m_end = end; }
|
||||
void SetPeriod(uint32_t const period) { m_period = period; }
|
||||
void SetPlus(bool const plus) { m_plus = plus; }
|
||||
|
||||
bool operator==(MonthdayRange const & rhs) const;
|
||||
|
||||
private:
|
||||
MonthDay m_start;
|
||||
MonthDay m_end;
|
||||
uint32_t m_period = 0;
|
||||
bool m_plus = false;
|
||||
};
|
||||
|
||||
using TMonthdayRanges = std::vector<MonthdayRange>;
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, MonthdayRange const & range);
|
||||
std::ostream & operator<<(std::ostream & ost, TMonthdayRanges const & ranges);
|
||||
|
||||
class YearRange
|
||||
{
|
||||
public:
|
||||
using TYear = uint16_t;
|
||||
|
||||
bool IsEmpty() const { return !HasStart() && !HasEnd(); }
|
||||
bool IsOpen() const { return HasStart() && !HasEnd(); }
|
||||
bool HasStart() const { return GetStart() != 0; }
|
||||
bool HasEnd() const { return GetEnd() != 0; }
|
||||
bool HasPlus() const { return m_plus; }
|
||||
bool HasPeriod() const { return GetPeriod() != 0; }
|
||||
|
||||
TYear GetStart() const { return m_start; }
|
||||
TYear GetEnd() const { return m_end; }
|
||||
uint32_t GetPeriod() const { return m_period; }
|
||||
|
||||
void SetStart(TYear const start) { m_start = start; }
|
||||
void SetEnd(TYear const end) { m_end = end; }
|
||||
void SetPlus(bool const plus) { m_plus = plus; }
|
||||
void SetPeriod(uint32_t const period) { m_period = period; }
|
||||
|
||||
bool operator==(YearRange const & rhs) const;
|
||||
|
||||
private:
|
||||
TYear m_start = 0;
|
||||
TYear m_end = 0;
|
||||
bool m_plus = false;
|
||||
uint32_t m_period = 0;
|
||||
};
|
||||
|
||||
using TYearRanges = std::vector<YearRange>;
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, YearRange const range);
|
||||
std::ostream & operator<<(std::ostream & ost, TYearRanges const ranges);
|
||||
|
||||
class WeekRange
|
||||
{
|
||||
public:
|
||||
using TWeek = uint8_t;
|
||||
|
||||
bool IsEmpty() const { return !HasStart() && !HasEnd(); }
|
||||
bool IsOpen() const { return HasStart() && !HasEnd(); }
|
||||
bool HasStart() const { return GetStart() != 0; }
|
||||
bool HasEnd() const { return GetEnd() != 0; }
|
||||
bool HasPeriod() const { return GetPeriod() != 0; }
|
||||
|
||||
TWeek GetStart() const { return m_start; }
|
||||
TWeek GetEnd() const { return m_end; }
|
||||
uint32_t GetPeriod() const { return m_period; }
|
||||
|
||||
void SetStart(TWeek const start) { m_start = start; }
|
||||
void SetEnd(TWeek const end) { m_end = end; }
|
||||
void SetPeriod(uint32_t const period) { m_period = period; }
|
||||
|
||||
bool operator==(WeekRange const & rhs) const;
|
||||
|
||||
private:
|
||||
TWeek m_start = 0;
|
||||
TWeek m_end = 0;
|
||||
uint32_t m_period = 0;
|
||||
};
|
||||
|
||||
using TWeekRanges = std::vector<WeekRange>;
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, WeekRange const range);
|
||||
std::ostream & operator<<(std::ostream & ost, TWeekRanges const ranges);
|
||||
|
||||
class RuleSequence
|
||||
{
|
||||
public:
|
||||
enum class Modifier
|
||||
{
|
||||
DefaultOpen,
|
||||
Open,
|
||||
Closed,
|
||||
Unknown,
|
||||
Comment
|
||||
};
|
||||
|
||||
bool IsEmpty() const { return !HasYears() && !HasMonths() && !HasWeeks() &&
|
||||
!HasWeekdays() && !HasTimes(); }
|
||||
bool IsTwentyFourHours() const { return m_twentyFourHours; }
|
||||
|
||||
bool HasYears() const { return !GetYears().empty(); }
|
||||
bool HasMonths() const { return !GetMonths().empty(); }
|
||||
bool HasMonthDay() const;
|
||||
bool HasWeeks() const { return !GetWeeks().empty(); }
|
||||
bool HasWeekdays() const { return !GetWeekdays().IsEmpty(); }
|
||||
bool HasTimes() const { return !GetTimes().empty(); }
|
||||
bool HasComment() const { return !GetComment().empty(); }
|
||||
bool HasModifierComment() const { return !GetModifierComment().empty(); }
|
||||
bool HasSeparatorForReadability() const { return m_separatorForReadability; }
|
||||
|
||||
TYearRanges const & GetYears() const { return m_years; }
|
||||
TMonthdayRanges const & GetMonths() const { return m_months; }
|
||||
TWeekRanges const & GetWeeks() const { return m_weeks; }
|
||||
Weekdays const & GetWeekdays() const { return m_weekdays; }
|
||||
TTimespans const & GetTimes() const { return m_times; }
|
||||
|
||||
std::string const & GetComment() const { return m_comment; }
|
||||
std::string const & GetModifierComment() const { return m_modifierComment; }
|
||||
std::string const & GetAnySeparator() const { return m_anySeparator; }
|
||||
|
||||
Modifier GetModifier() const { return m_modifier; }
|
||||
|
||||
void SetTwentyFourHours(bool const on) { m_twentyFourHours = on; }
|
||||
void SetYears(TYearRanges const & years) { m_years = years; }
|
||||
void SetMonths(TMonthdayRanges const & months) { m_months = months; }
|
||||
void SetWeeks(TWeekRanges const & weeks) { m_weeks = weeks; }
|
||||
|
||||
void SetWeekdays(Weekdays const & weekdays) { m_weekdays = weekdays; }
|
||||
void SetTimes(TTimespans const & times) { m_times = times; }
|
||||
|
||||
void SetComment(std::string const & comment) { m_comment = comment; }
|
||||
void SetModifierComment(std::string & comment) { m_modifierComment = comment; }
|
||||
void SetAnySeparator(std::string const & separator) { m_anySeparator = separator; }
|
||||
void SetSeparatorForReadability(bool const on) { m_separatorForReadability = on; }
|
||||
|
||||
void SetModifier(Modifier const modifier) { m_modifier = modifier; }
|
||||
|
||||
bool operator==(RuleSequence const & rhs) const;
|
||||
|
||||
private:
|
||||
bool m_twentyFourHours{false};
|
||||
|
||||
TYearRanges m_years;
|
||||
TMonthdayRanges m_months;
|
||||
TWeekRanges m_weeks;
|
||||
|
||||
Weekdays m_weekdays;
|
||||
TTimespans m_times;
|
||||
|
||||
std::string m_comment;
|
||||
std::string m_anySeparator = ";";
|
||||
bool m_separatorForReadability = false;
|
||||
|
||||
Modifier m_modifier = Modifier::DefaultOpen;
|
||||
std::string m_modifierComment;
|
||||
};
|
||||
|
||||
using TRuleSequences = std::vector<RuleSequence>;
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, RuleSequence::Modifier const modifier);
|
||||
std::ostream & operator<<(std::ostream & ost, RuleSequence const & sequence);
|
||||
std::ostream & operator<<(std::ostream & ost, TRuleSequences const & sequences);
|
||||
|
||||
enum class RuleState
|
||||
{
|
||||
Open,
|
||||
Closed,
|
||||
Unknown
|
||||
};
|
||||
|
||||
class OpeningHours
|
||||
{
|
||||
public:
|
||||
OpeningHours() = default;
|
||||
OpeningHours(std::string const & rule);
|
||||
OpeningHours(TRuleSequences const & rule);
|
||||
|
||||
bool IsOpen(time_t const dateTime) const;
|
||||
bool IsClosed(time_t const dateTime) const;
|
||||
bool IsUnknown(time_t const dateTime) const;
|
||||
|
||||
struct InfoT
|
||||
{
|
||||
RuleState state;
|
||||
/// Calculated only if state != RuleState::Unknown.
|
||||
time_t nextTimeOpen;
|
||||
time_t nextTimeClosed;
|
||||
};
|
||||
|
||||
InfoT GetInfo(time_t const dateTime) const;
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
bool IsTwentyFourHours() const;
|
||||
bool HasWeekdaySelector() const;
|
||||
bool HasMonthSelector() const;
|
||||
bool HasWeekSelector() const;
|
||||
bool HasYearSelector() const;
|
||||
|
||||
TRuleSequences const & GetRule() const { return m_rule; }
|
||||
|
||||
friend void swap(OpeningHours & lhs, OpeningHours & rhs);
|
||||
|
||||
bool operator==(OpeningHours const & rhs) const;
|
||||
|
||||
private:
|
||||
TRuleSequences m_rule;
|
||||
bool m_valid = false;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, OpeningHours const & oh);
|
||||
std::string ToString(osmoh::OpeningHours const & openingHours);
|
||||
} // namespace osmoh
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
project(opening_hours_integration_tests)
|
||||
|
||||
set(SRC opening_hours_integration_tests.cpp)
|
||||
|
||||
omim_add_test(${PROJECT_NAME} ${SRC} BOOST_TEST)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} opening_hours)
|
||||
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/opening-count.lst" "${CMAKE_BINARY_DIR}/"
|
||||
COMMENT "Copying opening-count.lst file for testing"
|
||||
)
|
||||
155961
3party/opening_hours/opening_hours_integration_tests/opening-count.lst
Normal file
155961
3party/opening_hours/opening_hours_integration_tests/opening-count.lst
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,208 @@
|
|||
#include "parse_opening_hours.hpp"
|
||||
#include "rules_evaluation.hpp"
|
||||
|
||||
#define BOOST_TEST_MODULE OpeningHoursIntegration
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
std::string ToString(T const & t)
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << t;
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool HasPeriod(std::vector<T> const & v)
|
||||
{
|
||||
auto const hasPeriod = [](T const & t) { return t.HasPeriod(); };
|
||||
return std::any_of(begin(v), end(v), hasPeriod);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool HasPlus(std::vector<T> const & v)
|
||||
{
|
||||
auto const hasPlus = [](T const & t) { return t.HasPlus(); };
|
||||
return std::any_of(begin(v), end(v), hasPlus);
|
||||
}
|
||||
|
||||
bool HasOffset(osmoh::TMonthdayRanges const & mr)
|
||||
{
|
||||
auto const hasOffset = [](osmoh::MonthdayRange const & md) {
|
||||
return
|
||||
md.GetStart().HasOffset() ||
|
||||
md.GetEnd().HasOffset();
|
||||
};
|
||||
return std::any_of(begin(mr), end(mr), hasOffset);
|
||||
}
|
||||
|
||||
bool HasOffset(osmoh::Weekdays const & wd)
|
||||
{
|
||||
auto const hasOffset = [](osmoh::WeekdayRange const & w) { return w.HasOffset(); };
|
||||
return std::any_of(begin(wd.GetWeekdayRanges()), end(wd.GetWeekdayRanges()), hasOffset);
|
||||
}
|
||||
|
||||
template <typename ParserResult>
|
||||
bool CompareNormalized(std::string const & original, ParserResult const & pretendent)
|
||||
{
|
||||
auto originalCopy = original;
|
||||
auto pretendentCopy = ToString(pretendent);
|
||||
|
||||
boost::to_lower(originalCopy);
|
||||
boost::to_lower(pretendentCopy);
|
||||
|
||||
boost::replace_all(originalCopy, "off", "closed");
|
||||
|
||||
boost::replace_all(originalCopy, " ", "");
|
||||
boost::replace_all(pretendentCopy, " ", "");
|
||||
|
||||
return pretendentCopy == originalCopy;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
Parsed,
|
||||
Serialised,
|
||||
Period,
|
||||
Plus,
|
||||
Offset,
|
||||
Count_
|
||||
};
|
||||
using TRuleFeatures = std::array<bool, Count_>;
|
||||
|
||||
std::ostream & operator<<(std::ostream & ost, TRuleFeatures const & f)
|
||||
{
|
||||
std::copy(std::begin(f), std::end(f), std::ostream_iterator<bool>(ost, "\t"));
|
||||
return ost;
|
||||
}
|
||||
|
||||
TRuleFeatures DescribeRule(osmoh::TRuleSequences const & rule)
|
||||
{
|
||||
TRuleFeatures features{};
|
||||
for (auto const & r : rule)
|
||||
{
|
||||
features[Period] |= HasPeriod(r.GetTimes());
|
||||
features[Period] |= HasPeriod(r.GetMonths());
|
||||
features[Period] |= HasPeriod(r.GetYears());
|
||||
features[Period] |= HasPeriod(r.GetWeeks());
|
||||
|
||||
features[Plus] |= HasPlus(r.GetTimes());
|
||||
features[Plus] |= HasPlus(r.GetMonths());
|
||||
features[Plus] |= HasPlus(r.GetYears());
|
||||
|
||||
features[Offset] |= HasOffset(r.GetMonths());
|
||||
features[Offset] |= HasOffset(r.GetWeekdays());
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/// How to run:
|
||||
/// 1. copy opening-count.lst to where the binary is
|
||||
/// 2. run with --log_level=message
|
||||
BOOST_AUTO_TEST_CASE(OpeningHours_CountFailed)
|
||||
{
|
||||
std::ifstream datalist("opening-count.lst");
|
||||
BOOST_REQUIRE_MESSAGE(datalist.is_open(),
|
||||
"Can't open ./opening-count.lst: " << std::strerror(errno));
|
||||
|
||||
std::string line;
|
||||
|
||||
size_t line_num = 0;
|
||||
size_t num_failed = 0;
|
||||
size_t num_total = 0;
|
||||
|
||||
std::map<size_t, size_t> hist;
|
||||
std::map<TRuleFeatures, size_t> featuresDistrib;
|
||||
|
||||
while (std::getline(datalist, line))
|
||||
{
|
||||
size_t count = 1;
|
||||
std::string datastr;
|
||||
|
||||
auto d = line.find('|');
|
||||
if (d == std::string::npos)
|
||||
{
|
||||
BOOST_WARN_MESSAGE((d != std::string::npos),
|
||||
"Incorrect line " << line_num << " format: " << line);
|
||||
datastr = line;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = std::stol(line.substr(0, d));
|
||||
datastr = line.substr(d + 1);
|
||||
}
|
||||
|
||||
line_num++;
|
||||
|
||||
osmoh::TRuleSequences rule;
|
||||
auto const isParsed = Parse(datastr, rule);
|
||||
TRuleFeatures features{};
|
||||
|
||||
if (isParsed)
|
||||
features = DescribeRule(rule);
|
||||
features[Parsed] = true;
|
||||
features[Serialised] = true;
|
||||
|
||||
if (!isParsed)
|
||||
{
|
||||
num_failed += count;
|
||||
++hist[count];
|
||||
features[Parsed] = false;
|
||||
features[Serialised] = false;
|
||||
BOOST_TEST_MESSAGE("-- " << count << " :[" << datastr << "]");
|
||||
}
|
||||
else if (!CompareNormalized(datastr, rule))
|
||||
{
|
||||
num_failed += count;
|
||||
++hist[count];
|
||||
features[Serialised] = false;
|
||||
BOOST_TEST_MESSAGE("- " << count << " :[" << datastr << "]");
|
||||
BOOST_TEST_MESSAGE("+ " << count << " :[" << ToString(rule) << "]");
|
||||
}
|
||||
|
||||
featuresDistrib[features] += count;
|
||||
num_total += count;
|
||||
}
|
||||
|
||||
BOOST_CHECK_MESSAGE((num_failed == 0),
|
||||
"Failed " << num_failed <<
|
||||
" of " << num_total <<
|
||||
" (" << double(num_failed)/(double(num_total)/100) << "%)");
|
||||
|
||||
{
|
||||
std::stringstream message;
|
||||
for (auto const & e : hist)
|
||||
message << "Weight: " << e.first << " Count: " << e.second << std::endl;
|
||||
|
||||
BOOST_TEST_MESSAGE(message.str());
|
||||
}
|
||||
{
|
||||
std::stringstream message;
|
||||
message << "Parsed\tSerialised\tPeriod\tPlus\tOffset\tCount" << std::endl;
|
||||
for (auto const & e : featuresDistrib)
|
||||
message << e.first << '\t' << e.second << std::endl;
|
||||
|
||||
BOOST_TEST_MESSAGE(message.str());
|
||||
}
|
||||
}
|
||||
114
3party/opening_hours/opening_hours_parsers.hpp
Normal file
114
3party/opening_hours/opening_hours_parsers.hpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#pragma once
|
||||
|
||||
#include "opening_hours.hpp"
|
||||
|
||||
// #define BOOST_SPIRIT_DEBUG
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
namespace phx = boost::phoenix;
|
||||
|
||||
namespace parsing
|
||||
{
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace charset = boost::spirit::standard_wide;
|
||||
|
||||
using space_type = charset::space_type;
|
||||
|
||||
using Iterator = std::string::const_iterator;
|
||||
|
||||
struct dash_ : public qi::symbols<char> { dash_(); };
|
||||
struct event_ : public qi::symbols<char, osmoh::TimeEvent::Event> { event_(); };
|
||||
struct wdays_ : qi::symbols<char, osmoh::Weekday> { wdays_(); };
|
||||
struct month_ : qi::symbols<char, osmoh::MonthDay::Month> { month_(); };
|
||||
struct hours_ : qi::symbols<char, osmoh::Time::THours> { hours_(); };
|
||||
struct exthours_ : qi::symbols<char, osmoh::Time::THours> { exthours_(); };
|
||||
struct minutes_ : qi::symbols<char, osmoh::Time::TMinutes> { minutes_(); };
|
||||
struct weeknum_ : qi::symbols<char, unsigned> { weeknum_(); };
|
||||
struct daynum_ : qi::symbols<char, MonthDay::TDayNum> { daynum_(); };
|
||||
|
||||
extern dash_ dash;
|
||||
extern event_ event;
|
||||
extern wdays_ wdays;
|
||||
extern month_ month;
|
||||
extern hours_ hours;
|
||||
extern exthours_ exthours;
|
||||
extern minutes_ minutes;
|
||||
extern weeknum_ weeknum;
|
||||
extern daynum_ daynum;
|
||||
|
||||
|
||||
class year_selector_parser : public qi::grammar<Iterator, osmoh::TYearRanges(), space_type>
|
||||
{
|
||||
protected:
|
||||
qi::rule<Iterator, osmoh::YearRange(), space_type> year_range;
|
||||
qi::rule<Iterator, osmoh::TYearRanges(), space_type> main;
|
||||
|
||||
public:
|
||||
year_selector_parser();
|
||||
};
|
||||
|
||||
class week_selector_parser : public qi::grammar<Iterator, osmoh::TWeekRanges(), space_type>
|
||||
{
|
||||
protected:
|
||||
qi::rule<Iterator, osmoh::WeekRange(), space_type> week;
|
||||
qi::rule<Iterator, osmoh::TWeekRanges(), space_type> main;
|
||||
|
||||
public:
|
||||
week_selector_parser();
|
||||
};
|
||||
|
||||
class month_selector_parser : public qi::grammar<Iterator, TMonthdayRanges(), space_type>
|
||||
{
|
||||
protected:
|
||||
qi::rule<Iterator, int32_t(), space_type, qi::locals<int32_t>> day_offset;
|
||||
qi::rule<Iterator, DateOffset(), space_type, qi::locals<bool>> date_offset;
|
||||
|
||||
qi::rule<Iterator, MonthDay(), space_type> date_left;
|
||||
qi::rule<Iterator, MonthDay(), space_type> date_right;
|
||||
qi::rule<Iterator, MonthDay(), space_type> date_from;
|
||||
qi::rule<Iterator, MonthDay(), space_type> date_to;
|
||||
qi::rule<Iterator, MonthDay(), space_type> date_from_with_offset;
|
||||
qi::rule<Iterator, MonthDay(), space_type> date_to_with_offset;
|
||||
|
||||
qi::rule<Iterator, MonthdayRange(), space_type> monthday_range;
|
||||
qi::rule<Iterator, TMonthdayRanges(), space_type> main;
|
||||
|
||||
public:
|
||||
month_selector_parser();
|
||||
};
|
||||
|
||||
class weekday_selector_parser : public qi::grammar<Iterator, osmoh::Weekdays(), space_type>
|
||||
{
|
||||
protected:
|
||||
qi::rule<Iterator, osmoh::NthWeekdayOfTheMonthEntry::NthDayOfTheMonth(), space_type> nth;
|
||||
qi::rule<Iterator, osmoh::NthWeekdayOfTheMonthEntry(), space_type> nth_entry;
|
||||
qi::rule<Iterator, int32_t(), space_type, qi::locals<int8_t>> day_offset;
|
||||
qi::rule<Iterator, osmoh::WeekdayRange(), space_type> weekday_range;
|
||||
qi::rule<Iterator, osmoh::TWeekdayRanges(), space_type> weekday_sequence;
|
||||
qi::rule<Iterator, osmoh::Holiday(), space_type> holiday;
|
||||
qi::rule<Iterator, osmoh::THolidays(), space_type> holiday_sequence;
|
||||
qi::rule<Iterator, osmoh::Weekdays(), space_type> main;
|
||||
|
||||
public:
|
||||
weekday_selector_parser();
|
||||
};
|
||||
|
||||
class time_selector_parser : public qi::grammar<Iterator, osmoh::TTimespans(), space_type>
|
||||
{
|
||||
protected:
|
||||
qi::rule<Iterator, osmoh::HourMinutes(), space_type> hour_minutes;
|
||||
qi::rule<Iterator, osmoh::HourMinutes(), space_type> extended_hour_minutes;
|
||||
qi::rule<Iterator, osmoh::TimeEvent(), space_type> variable_time;
|
||||
qi::rule<Iterator, osmoh::Time(), space_type> extended_time;
|
||||
qi::rule<Iterator, osmoh::Time(), space_type> time;
|
||||
qi::rule<Iterator, osmoh::Timespan(), space_type> timespan;
|
||||
qi::rule<Iterator, osmoh::TTimespans(), space_type> main;
|
||||
|
||||
public:
|
||||
time_selector_parser();
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace osmoh
|
||||
111
3party/opening_hours/opening_hours_parsers_terminals.cpp
Normal file
111
3party/opening_hours/opening_hours_parsers_terminals.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
#include "opening_hours_parsers.hpp"
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
namespace parsing
|
||||
{
|
||||
dash_::dash_()
|
||||
{
|
||||
add
|
||||
("-")
|
||||
/* not standard */
|
||||
// (L"–")(L"—")(L"-")(L"~")(L"~")(L"〜")(L"to")(L"às")(L"ás")(L"as")(L"a")(L"ate")(L"bis")
|
||||
;
|
||||
}
|
||||
|
||||
event_::event_()
|
||||
{
|
||||
add
|
||||
("dawn", osmoh::TimeEvent::Event::Sunrise)
|
||||
("sunrise", osmoh::TimeEvent::Event::Sunrise)
|
||||
("sunset", osmoh::TimeEvent::Event::Sunset)
|
||||
("dusk", osmoh::TimeEvent::Event::Sunset)
|
||||
;
|
||||
}
|
||||
|
||||
wdays_::wdays_()
|
||||
{
|
||||
add
|
||||
("su", 1_weekday)("mo", 2_weekday)("tu", 3_weekday)("we", 4_weekday)("th", 5_weekday)("fr", 6_weekday)("sa", 7_weekday) // en
|
||||
// (L"mon", 0)(L"tue", 1)(L"wed", 2)(L"thu", 3)(L"fri", 4)(L"sat", 5)(L"sun", 6) // en
|
||||
// (L"пн", 0)(L"вт", 1)(L"ср", 2)(L"чт", 3)(L"пт", 4)(L"сб", 5)(L"вс", 6) // ru
|
||||
// (L"пн.", 0)(L"вт.", 1)(L"ср.", 2)(L"чт.", 3)(L"пт.", 4)(L"сб.", 5)(L"вс.", 6) // ru
|
||||
// (L"lu", 0)(L"ma", 1)(L"me", 2)(L"je", 3)(L"ve", 4)(L"sa", 5)(L"di", 6) // fr
|
||||
// (L"lu", 0)(L"ma", 1)(L"me", 2)(L"gi", 3)(L"ve", 4)(L"sa", 5)(L"do", 6) // it
|
||||
// (L"lu", 0)(L"ma", 1)(L"mi", 2)(L"ju", 3)(L"vie", 4)(L"sá", 5)(L"do", 6) // sp
|
||||
// (L"週一", 0)(L"週二", 1)(L"週三", 2)(L"週四", 3)(L"週五", 4)(L"週六", 5)(L"週日", 6) // ch traditional
|
||||
// (L"senin", 0)(L"selasa", 1)(L"rabu", 2)(L"kamis", 3)(L"jum'at", 4)(L"sabtu", 5)(L"minggu", 6) // indonesian
|
||||
|
||||
// (L"wd", 2)
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
month_::month_()
|
||||
{
|
||||
add
|
||||
("jan", 1_M)("feb", 2_M)("mar", 3_M)("apr", 4_M)("may", 5_M)("jun", 6_M)
|
||||
("jul", 7_M)("aug", 8_M)("sep", 9_M)("oct", 10_M)("nov", 11_M)("dec", 12_M)
|
||||
;
|
||||
}
|
||||
|
||||
hours_::hours_()
|
||||
{
|
||||
add
|
||||
( "0", 0_h)( "1", 1_h)( "2", 2_h)( "3", 3_h)( "4", 4_h)( "5", 5_h)( "6", 6_h)( "7", 7_h)( "8", 8_h)( "9", 9_h) /* not standard */
|
||||
("00", 0_h)("01", 1_h)("02", 2_h)("03", 3_h)("04", 4_h)("05", 5_h)("06", 6_h)("07", 7_h)("08", 8_h)("09", 9_h)
|
||||
("10", 10_h)("11", 11_h)("12", 12_h)("13", 13_h)("14", 14_h)("15", 15_h)("16", 16_h)("17", 17_h)("18", 18_h)("19", 19_h)
|
||||
("20", 20_h)("21", 21_h)("22", 22_h)("23", 23_h)("24", 24_h)
|
||||
;
|
||||
}
|
||||
|
||||
exthours_::exthours_()
|
||||
{
|
||||
add
|
||||
( "0", 0_h)( "1", 1_h)( "2", 2_h)( "3", 3_h)( "4", 4_h)( "5", 5_h)( "6", 6_h)( "7", 7_h)( "8", 8_h)( "9", 9_h) /* not standard */
|
||||
("00", 0_h)("01", 1_h)("02", 2_h)("03", 3_h)("04", 4_h)("05", 5_h)("06", 6_h)("07", 7_h)("08", 8_h)("09", 9_h)
|
||||
("10", 10_h)("11", 11_h)("12", 12_h)("13", 13_h)("14", 14_h)("15", 15_h)("16", 16_h)("17", 17_h)("18", 18_h)("19", 19_h)
|
||||
("20", 20_h)("21", 21_h)("22", 22_h)("23", 23_h)("24", 24_h)("25", 25_h)("26", 26_h)("27", 27_h)("28", 28_h)("29", 29_h)
|
||||
("30", 30_h)("31", 31_h)("32", 32_h)("33", 33_h)("34", 34_h)("35", 35_h)("36", 36_h)("37", 37_h)("38", 38_h)("39", 39_h)
|
||||
("40", 40_h)("41", 41_h)("42", 42_h)("43", 43_h)("44", 44_h)("45", 45_h)("46", 46_h)("47", 47_h)("48", 48_h)
|
||||
;
|
||||
}
|
||||
|
||||
minutes_::minutes_()
|
||||
{
|
||||
add
|
||||
( "0", 0_min)( "1", 1_min)( "2", 2_min)( "3", 3_min)( "4", 4_min)( "5", 5_min)( "6", 6_min)( "7", 7_min)( "8", 8_min)( "9", 9_min) /* not standard */
|
||||
("00", 0_min)("01", 1_min)("02", 2_min)("03", 3_min)("04", 4_min)("05", 5_min)("06", 6_min)("07", 7_min)("08", 8_min)("09", 9_min)
|
||||
("10", 10_min)("11", 11_min)("12", 12_min)("13", 13_min)("14", 14_min)("15", 15_min)("16", 16_min)("17", 17_min)("18", 18_min)("19", 19_min)
|
||||
("20", 20_min)("21", 21_min)("22", 22_min)("23", 23_min)("24", 24_min)("25", 25_min)("26", 26_min)("27", 27_min)("28", 28_min)("29", 29_min)
|
||||
("30", 30_min)("31", 31_min)("32", 32_min)("33", 33_min)("34", 34_min)("35", 35_min)("36", 36_min)("37", 37_min)("38", 38_min)("39", 39_min)
|
||||
("40", 40_min)("41", 41_min)("42", 42_min)("43", 43_min)("44", 44_min)("45", 45_min)("46", 46_min)("47", 47_min)("48", 48_min)("49", 49_min)
|
||||
("50", 50_min)("51", 51_min)("52", 52_min)("53", 53_min)("54", 54_min)("55", 55_min)("56", 56_min)("57", 57_min)("58", 58_min)("59", 59_min)
|
||||
;
|
||||
}
|
||||
|
||||
weeknum_::weeknum_()
|
||||
{
|
||||
add
|
||||
( "1", 1)( "2", 2)( "3", 3)( "4", 4)( "5", 5)( "6", 6)( "7", 7)( "8", 8)( "9", 9)
|
||||
("01", 1)("02", 2)("03", 3)("04", 4)("05", 5)("06", 6)("07", 7)("08", 8)("09", 9)
|
||||
("10", 10)("11", 11)("12", 12)("13", 13)("14", 14)("15", 15)("16", 16)("17", 17)("18", 18)("19", 19)
|
||||
("20", 20)("21", 21)("22", 22)("23", 23)("24", 24)("25", 25)("26", 26)("27", 27)("28", 28)("29", 29)
|
||||
("30", 30)("31", 31)("32", 32)("33", 33)("34", 34)("35", 35)("36", 36)("37", 37)("38", 38)("39", 39)
|
||||
("40", 40)("41", 41)("42", 42)("43", 43)("44", 44)("45", 45)("46", 46)("47", 47)("48", 48)("49", 49)
|
||||
("50", 50)("51", 51)("52", 52)("53", 53)
|
||||
;
|
||||
}
|
||||
|
||||
daynum_::daynum_()
|
||||
{
|
||||
add
|
||||
("1", 1)("2", 2)("3", 3)("4", 4)("5", 5)("6", 6)("7", 7)("8", 8)("9", 9)
|
||||
("01", 1)("02", 2)("03", 3)("04", 4)("05", 5)("06", 6)("07", 7)("08", 8)("09", 9)
|
||||
("10", 10)("11", 11)("12", 12)("13", 13)("14", 14)("15", 15)("16", 16)("17", 17)("18", 18)("19", 19)
|
||||
("20", 20)("21", 21)("22", 22)("23", 23)("24", 24)("25", 25)("26", 26)("27", 27)("28", 28)("29", 29)
|
||||
("30", 30)("31", 31)
|
||||
;
|
||||
}
|
||||
} // namespace parsing
|
||||
} // namespace osmoh
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
project(opening_hours_supported_features_tests)
|
||||
|
||||
set(SRC opening_hours_supported_features_tests.cpp)
|
||||
|
||||
omim_add_test(${PROJECT_NAME} ${SRC} BOOST_TEST)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} opening_hours)
|
||||
File diff suppressed because it is too large
Load diff
7
3party/opening_hours/opening_hours_tests/CMakeLists.txt
Normal file
7
3party/opening_hours/opening_hours_tests/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
project(opening_hours_tests)
|
||||
|
||||
set(SRC opening_hours_tests.cpp)
|
||||
|
||||
omim_add_test(${PROJECT_NAME} ${SRC} BOOST_TEST)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} opening_hours)
|
||||
1786
3party/opening_hours/opening_hours_tests/opening_hours_tests.cpp
Normal file
1786
3party/opening_hours/opening_hours_tests/opening_hours_tests.cpp
Normal file
File diff suppressed because it is too large
Load diff
109
3party/opening_hours/parse_months.cpp
Normal file
109
3party/opening_hours/parse_months.cpp
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
#include "parse_opening_hours.hpp"
|
||||
#include "opening_hours_parsers.hpp"
|
||||
|
||||
#include <boost/phoenix/bind.hpp>
|
||||
#include <boost/phoenix/operator.hpp> // operator,
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
namespace parsing
|
||||
{
|
||||
month_selector_parser::month_selector_parser() : month_selector_parser::base_type(main)
|
||||
{
|
||||
using qi::_1;
|
||||
using qi::_2;
|
||||
using qi::_3;
|
||||
using qi::_a;
|
||||
using qi::_val;
|
||||
using qi::uint_;
|
||||
using qi::ushort_;
|
||||
using qi::lit;
|
||||
using qi::double_;
|
||||
using qi::lexeme;
|
||||
using osmoh::DateOffset;
|
||||
using osmoh::MonthDay;
|
||||
using osmoh::MonthdayRange;
|
||||
|
||||
static const qi::int_parser<unsigned, 10, 4, 4> year = {};
|
||||
|
||||
day_offset = ((lit('+')[_a = 1] | lit('-')[_a = -1]) >>
|
||||
ushort_ >> charset::no_case[(lit("days") | lit("day"))]) [_val = _a * _1];
|
||||
|
||||
date_offset = ((lit('+')[_a = true] | lit('-')[_a = false])
|
||||
>> charset::no_case[wdays] >> day_offset)
|
||||
[(bind(&DateOffset::SetWDayOffset, _val, _1),
|
||||
bind(&DateOffset::SetOffset, _val, _2),
|
||||
bind(&DateOffset::SetWDayOffsetPositive, _val, _a))]
|
||||
| ((lit('+')[_a = true] | lit('-') [_a = false]) >> charset::no_case[wdays])
|
||||
[(bind(&DateOffset::SetWDayOffset, _val, _1),
|
||||
bind(&DateOffset::SetWDayOffsetPositive, _val, _a))]
|
||||
| day_offset [bind(&DateOffset::SetOffset, _val, _1)]
|
||||
;
|
||||
|
||||
date_left = (year >> charset::no_case[month]) [(bind(&MonthDay::SetYear, _val, _1),
|
||||
bind(&MonthDay::SetMonth, _val, _2))]
|
||||
|
||||
| charset::no_case[month] [bind(&MonthDay::SetMonth, _val, _1)]
|
||||
;
|
||||
|
||||
date_right = charset::no_case[month] [bind(&MonthDay::SetMonth, _val, _1)]
|
||||
;
|
||||
|
||||
date_from = (date_left >> (daynum >> !(lit(':') >> qi::digit)))
|
||||
[(_val = _1, bind(&MonthDay::SetDayNum, _val, _2))]
|
||||
| (year >> charset::no_case[lit("easter")]) [(bind(&MonthDay::SetYear, _val, _1),
|
||||
bind(&MonthDay::SetVariableDate, _val,
|
||||
MonthDay::VariableDate::Easter))]
|
||||
| charset::no_case[lit("easter")] [bind(&MonthDay::SetVariableDate, _val,
|
||||
MonthDay::VariableDate::Easter)]
|
||||
;
|
||||
|
||||
date_to = date_from [_val = _1]
|
||||
| (daynum >> !(lit(':') >> qi::digit)) [bind(&MonthDay::SetDayNum, _val, _1)]
|
||||
;
|
||||
|
||||
date_from_with_offset = (date_from >> date_offset)
|
||||
[(_val = _1, bind(&MonthDay::SetOffset, _val, _2))]
|
||||
| date_from [_val = _1]
|
||||
;
|
||||
|
||||
date_to_with_offset = (date_to >> date_offset)
|
||||
[(_val = _1, bind(&MonthDay::SetOffset, _val, _2))]
|
||||
| date_to [_val = _1]
|
||||
;
|
||||
|
||||
monthday_range = (date_from_with_offset >> dash >> date_to_with_offset)
|
||||
[(bind(&MonthdayRange::SetStart, _val, _1),
|
||||
bind(&MonthdayRange::SetEnd, _val, _2))]
|
||||
| (date_from_with_offset >> '+') [(bind(&MonthdayRange::SetStart, _val, _1),
|
||||
bind(&MonthdayRange::SetPlus, _val, true))]
|
||||
| (date_left >> dash >> date_right >> '/' >> uint_)
|
||||
[(bind(&MonthdayRange::SetStart, _val, _1),
|
||||
bind(&MonthdayRange::SetEnd, _val, _2),
|
||||
bind(&MonthdayRange::SetPeriod, _val, _3))]
|
||||
| (date_left >> lit("-") >> date_right) [(bind(&MonthdayRange::SetStart, _val, _1),
|
||||
bind(&MonthdayRange::SetEnd, _val, _2))]
|
||||
| date_from [bind(&MonthdayRange::SetStart, _val, _1)]
|
||||
| date_left [bind(&MonthdayRange::SetStart, _val, _1)]
|
||||
;
|
||||
|
||||
main %= (monthday_range % ',');
|
||||
|
||||
BOOST_SPIRIT_DEBUG_NODE(main);
|
||||
BOOST_SPIRIT_DEBUG_NODE(monthday_range);
|
||||
BOOST_SPIRIT_DEBUG_NODE(day_offset);
|
||||
BOOST_SPIRIT_DEBUG_NODE(date_offset);
|
||||
BOOST_SPIRIT_DEBUG_NODE(date_left);
|
||||
BOOST_SPIRIT_DEBUG_NODE(date_right);
|
||||
BOOST_SPIRIT_DEBUG_NODE(date_from);
|
||||
BOOST_SPIRIT_DEBUG_NODE(date_to);
|
||||
BOOST_SPIRIT_DEBUG_NODE(date_from_with_offset);
|
||||
BOOST_SPIRIT_DEBUG_NODE(date_to_with_offset);
|
||||
}
|
||||
}
|
||||
|
||||
bool Parse(std::string const & str, TMonthdayRanges & context)
|
||||
{
|
||||
return osmoh::ParseImpl<parsing::month_selector_parser>(str, context);
|
||||
}
|
||||
} // namespace osmoh
|
||||
123
3party/opening_hours/parse_opening_hours.cpp
Normal file
123
3party/opening_hours/parse_opening_hours.cpp
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
#include "parse_opening_hours.hpp"
|
||||
#include "opening_hours_parsers.hpp"
|
||||
|
||||
#include <boost/phoenix/bind.hpp>
|
||||
#include <boost/phoenix/operator.hpp> // operator,
|
||||
#include <boost/phoenix/stl.hpp>
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
namespace parsing
|
||||
{
|
||||
dash_ dash;
|
||||
event_ event;
|
||||
wdays_ wdays;
|
||||
month_ month;
|
||||
hours_ hours;
|
||||
exthours_ exthours;
|
||||
minutes_ minutes;
|
||||
weeknum_ weeknum;
|
||||
daynum_ daynum;
|
||||
|
||||
class time_domain : public qi::grammar<Iterator, osmoh::TRuleSequences(), space_type>
|
||||
{
|
||||
protected:
|
||||
weekday_selector_parser weekday_selector;
|
||||
time_selector_parser time_selector;
|
||||
year_selector_parser year_selector;
|
||||
month_selector_parser month_selector;
|
||||
week_selector_parser week_selector;
|
||||
|
||||
qi::rule<Iterator, std::string()> comment;
|
||||
qi::rule<Iterator, std::string(), space_type> separator;
|
||||
|
||||
qi::rule<Iterator, qi::unused_type(osmoh::RuleSequence &), space_type> small_range_selectors;
|
||||
qi::rule<Iterator, qi::unused_type(osmoh::RuleSequence &), space_type> wide_range_selectors;
|
||||
qi::rule<Iterator, qi::unused_type(osmoh::RuleSequence &), space_type> rule_modifier;
|
||||
|
||||
qi::rule<Iterator, osmoh::RuleSequence(), space_type> rule_sequence;
|
||||
qi::rule<Iterator, osmoh::TRuleSequences(), space_type> main;
|
||||
|
||||
public:
|
||||
time_domain() : time_domain::base_type(main)
|
||||
{
|
||||
using qi::lit;
|
||||
using qi::lexeme;
|
||||
using qi::_1;
|
||||
using qi::_a;
|
||||
using qi::_r1;
|
||||
using qi::_val;
|
||||
using charset::char_;
|
||||
using qi::eps;
|
||||
using qi::lazy;
|
||||
using phx::back;
|
||||
using phx::push_back;
|
||||
using osmoh::RuleSequence;
|
||||
|
||||
using Modifier = RuleSequence::Modifier;
|
||||
|
||||
comment %= '"' >> +(char_ - '"') >> '"'
|
||||
;
|
||||
|
||||
separator %= charset::string(";")
|
||||
| charset::string("||")
|
||||
| charset::string(",")
|
||||
;
|
||||
|
||||
wide_range_selectors =
|
||||
( -(year_selector [bind(&RuleSequence::SetYears, _r1, _1)]) >>
|
||||
-(month_selector [bind(&RuleSequence::SetMonths, _r1, _1)]) >>
|
||||
-(week_selector [bind(&RuleSequence::SetWeeks, _r1, _1)]) >>
|
||||
-(lit(':') [bind(&RuleSequence::SetSeparatorForReadability, _r1, true)]))
|
||||
| (comment >> ':') [bind(&RuleSequence::SetComment, _r1, _1)]
|
||||
;
|
||||
|
||||
small_range_selectors =
|
||||
( -(weekday_selector [bind(&RuleSequence::SetWeekdays, _r1, _1)]) >>
|
||||
-(time_selector [bind(&RuleSequence::SetTimes, _r1, _1)]))
|
||||
;
|
||||
|
||||
rule_modifier =
|
||||
(charset::no_case[lit("open") | lit("on")]
|
||||
[bind(&RuleSequence::SetModifier, _r1, Modifier::Open)] >>
|
||||
-(comment [bind(&RuleSequence::SetModifierComment, _r1, _1)]))
|
||||
|
||||
| ((charset::no_case[lit("closed") | lit("off")])
|
||||
[bind(&RuleSequence::SetModifier, _r1, Modifier::Closed)] >>
|
||||
-(comment [bind(&RuleSequence::SetModifierComment, _r1, _1)]))
|
||||
|
||||
| (charset::no_case[lit("unknown")]
|
||||
[bind(&RuleSequence::SetModifier, _r1, Modifier::Unknown)] >>
|
||||
-(comment [bind(&RuleSequence::SetModifierComment, _r1, _1)]))
|
||||
|
||||
| comment [(bind(&RuleSequence::SetModifier, _r1, Modifier::Comment),
|
||||
bind(&RuleSequence::SetModifierComment, _r1, _1))]
|
||||
;
|
||||
|
||||
rule_sequence =
|
||||
( lit("24/7") [bind(&RuleSequence::SetTwentyFourHours, _val, true)]
|
||||
| ( -wide_range_selectors(_val) >>
|
||||
-small_range_selectors(_val) )) >>
|
||||
-rule_modifier(_val)
|
||||
;
|
||||
|
||||
main = ( -(lit("opening_hours") >> lit('=')) >>
|
||||
(rule_sequence [push_back(_val, _1)] %
|
||||
(separator [phx::bind(&RuleSequence::SetAnySeparator, back(_val), _1)])))
|
||||
;
|
||||
|
||||
BOOST_SPIRIT_DEBUG_NODE(main);
|
||||
BOOST_SPIRIT_DEBUG_NODE(rule_sequence);
|
||||
BOOST_SPIRIT_DEBUG_NODE(rule_modifier);
|
||||
BOOST_SPIRIT_DEBUG_NODE(small_range_selectors);
|
||||
BOOST_SPIRIT_DEBUG_NODE(wide_range_selectors);
|
||||
}
|
||||
};
|
||||
} // namespace parsing
|
||||
|
||||
bool Parse(std::string const & str, TRuleSequences & context)
|
||||
{
|
||||
context.clear();
|
||||
return osmoh::ParseImpl<parsing::time_domain>(str, context);
|
||||
}
|
||||
} // namespace osmoh
|
||||
38
3party/opening_hours/parse_opening_hours.hpp
Normal file
38
3party/opening_hours/parse_opening_hours.hpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include "opening_hours.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
template<typename Parser, typename Context>
|
||||
bool ParseImpl(std::string const & str, Context & context)
|
||||
{
|
||||
using boost::spirit::qi::phrase_parse;
|
||||
using boost::spirit::standard_wide::space;
|
||||
|
||||
Parser parser;
|
||||
#ifndef NDEBUG
|
||||
boost::spirit::qi::what(parser);
|
||||
#endif
|
||||
|
||||
auto first = begin(str);
|
||||
auto const last = end(str);
|
||||
auto parsed = phrase_parse(first, last, parser, space, context);
|
||||
|
||||
if (!parsed || first != last)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Parse(std::string const &, TTimespans &);
|
||||
bool Parse(std::string const &, Weekdays &);
|
||||
bool Parse(std::string const &, TMonthdayRanges &);
|
||||
bool Parse(std::string const &, TYearRanges &);
|
||||
bool Parse(std::string const &, TWeekRanges &);
|
||||
bool Parse(std::string const &, TRuleSequences &);
|
||||
} // namespace osmoh
|
||||
97
3party/opening_hours/parse_timespans.cpp
Normal file
97
3party/opening_hours/parse_timespans.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#include "parse_opening_hours.hpp"
|
||||
#include "opening_hours_parsers.hpp"
|
||||
|
||||
#include <boost/phoenix/bind.hpp>
|
||||
#include <boost/phoenix/operator.hpp> // operator,
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
namespace parsing
|
||||
{
|
||||
time_selector_parser::time_selector_parser() : time_selector_parser::base_type(main)
|
||||
{
|
||||
using qi::int_;
|
||||
using qi::_1;
|
||||
using qi::_2;
|
||||
using qi::_3;
|
||||
using qi::_a;
|
||||
using qi::_val;
|
||||
using qi::lit;
|
||||
using charset::char_;
|
||||
using osmoh::HourMinutes;
|
||||
using osmoh::TimeEvent;
|
||||
using osmoh::Time;
|
||||
using osmoh::Timespan;
|
||||
|
||||
hour_minutes =
|
||||
(hours >> lit(':') >> minutes) [(bind(&HourMinutes::AddDuration, _val, _1),
|
||||
bind(&HourMinutes::AddDuration, _val, _2))]
|
||||
;
|
||||
|
||||
extended_hour_minutes =
|
||||
(exthours >> lit(':') >> minutes)[(bind(&HourMinutes::AddDuration, _val, _1),
|
||||
bind(&HourMinutes::AddDuration, _val, _2))]
|
||||
;
|
||||
|
||||
variable_time =
|
||||
( lit('(')
|
||||
>> charset::no_case[event] [bind(&TimeEvent::SetEvent, _val, _1)]
|
||||
>> ( (lit('+') >> hour_minutes) [bind(&TimeEvent::SetOffset, _val, _1)]
|
||||
| (lit('-') >> hour_minutes) [bind(&TimeEvent::SetOffset, _val, -_1)] )
|
||||
>> lit(')')
|
||||
)
|
||||
| charset::no_case[event][bind(&TimeEvent::SetEvent, _val, _1)]
|
||||
;
|
||||
|
||||
extended_time = extended_hour_minutes [bind(&Time::SetHourMinutes, _val, _1)]
|
||||
| variable_time [bind(&Time::SetEvent, _val, _1)]
|
||||
;
|
||||
|
||||
time = hour_minutes [bind(&Time::SetHourMinutes, _val, _1)]
|
||||
| variable_time [bind(&Time::SetEvent, _val, _1)]
|
||||
;
|
||||
|
||||
timespan =
|
||||
(time >> dash >> extended_time >> '/' >> hour_minutes)
|
||||
[(bind(&Timespan::SetStart, _val, _1),
|
||||
bind(&Timespan::SetEnd, _val, _2),
|
||||
bind(&Timespan::SetPeriod, _val, _3))]
|
||||
|
||||
| (time >> dash >> extended_time >> '/' >> minutes)
|
||||
[(bind(&Timespan::SetStart, _val, _1),
|
||||
bind(&Timespan::SetEnd, _val, _2),
|
||||
bind(&Timespan::SetPeriod, _val, _3))]
|
||||
|
||||
| (time >> dash >> extended_time >> '+')
|
||||
[(bind(&Timespan::SetStart, _val, _1),
|
||||
bind(&Timespan::SetEnd, _val, _2),
|
||||
bind(&Timespan::SetPlus, _val, true))]
|
||||
|
||||
| (time >> dash >> extended_time)
|
||||
[(bind(&Timespan::SetStart, _val, _1),
|
||||
bind(&Timespan::SetEnd, _val, _2))]
|
||||
|
||||
| (time >> '+')
|
||||
[(bind(&Timespan::SetStart, _val, _1),
|
||||
bind(&Timespan::SetPlus, _val, true))]
|
||||
|
||||
// This rule is only used for collection_times tag wish is not in our interest.
|
||||
// | time[bind(&Timespan::SetStart, _val, _1)]
|
||||
;
|
||||
|
||||
main %= timespan % ',';
|
||||
|
||||
BOOST_SPIRIT_DEBUG_NODE(main);
|
||||
BOOST_SPIRIT_DEBUG_NODE(timespan);
|
||||
BOOST_SPIRIT_DEBUG_NODE(time);
|
||||
BOOST_SPIRIT_DEBUG_NODE(extended_time);
|
||||
BOOST_SPIRIT_DEBUG_NODE(variable_time);
|
||||
BOOST_SPIRIT_DEBUG_NODE(extended_hour_minutes);
|
||||
}
|
||||
}
|
||||
|
||||
bool Parse(std::string const & str, TTimespans & context)
|
||||
{
|
||||
return osmoh::ParseImpl<parsing::time_selector_parser>(str, context);
|
||||
}
|
||||
} // namespace osmoh
|
||||
79
3party/opening_hours/parse_weekdays.cpp
Normal file
79
3party/opening_hours/parse_weekdays.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#include "parse_opening_hours.hpp"
|
||||
#include "opening_hours_parsers.hpp"
|
||||
|
||||
#include <boost/phoenix/bind.hpp>
|
||||
#include <boost/phoenix/operator.hpp> // operator,
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
namespace parsing
|
||||
{
|
||||
weekday_selector_parser::weekday_selector_parser() : weekday_selector_parser::base_type(main)
|
||||
{
|
||||
using qi::_a;
|
||||
using qi::_1;
|
||||
using qi::_2;
|
||||
using qi::_val;
|
||||
using qi::lit;
|
||||
using qi::ushort_;
|
||||
using osmoh::NthWeekdayOfTheMonthEntry;
|
||||
using osmoh::Holiday;
|
||||
using osmoh::WeekdayRange;
|
||||
using osmoh::Weekdays;
|
||||
|
||||
nth = ushort_(1)[_val = NthWeekdayOfTheMonthEntry::NthDayOfTheMonth::First]
|
||||
| ushort_(2) [_val = NthWeekdayOfTheMonthEntry::NthDayOfTheMonth::Second]
|
||||
| ushort_(3) [_val = NthWeekdayOfTheMonthEntry::NthDayOfTheMonth::Third]
|
||||
| ushort_(4) [_val = NthWeekdayOfTheMonthEntry::NthDayOfTheMonth::Fourth]
|
||||
| ushort_(5) [_val = NthWeekdayOfTheMonthEntry::NthDayOfTheMonth::Fifth];
|
||||
|
||||
nth_entry = (nth >> dash >> nth) [(bind(&NthWeekdayOfTheMonthEntry::SetStart, _val, _1),
|
||||
bind(&NthWeekdayOfTheMonthEntry::SetEnd, _val, _2))]
|
||||
| (lit('-') >> nth) [bind(&NthWeekdayOfTheMonthEntry::SetEnd, _val, _1)]
|
||||
| nth [bind(&NthWeekdayOfTheMonthEntry::SetStart, _val, _1)]
|
||||
;
|
||||
|
||||
day_offset =
|
||||
( (lit('+')[_a = 1] | lit('-') [_a = -1]) >>
|
||||
ushort_ [_val = _1 * _a] >>
|
||||
charset::no_case[(lit("days") | lit("day"))] )
|
||||
;
|
||||
|
||||
holiday = (charset::no_case[lit("SH")] [bind(&Holiday::SetPlural, _val, false)]
|
||||
>> -day_offset [bind(&Holiday::SetOffset, _val, _1)])
|
||||
| charset::no_case[lit("PH")] [bind(&Holiday::SetPlural, _val, true)]
|
||||
;
|
||||
|
||||
holiday_sequence %= (holiday % ',');
|
||||
|
||||
weekday_range =
|
||||
( charset::no_case[wdays] [bind(&WeekdayRange::SetStart, _val, _1)] >>
|
||||
'[' >> (nth_entry [bind(&WeekdayRange::AddNth, _val, _1)]) % ',') >> ']' >>
|
||||
-(day_offset [bind(&WeekdayRange::SetOffset, _val, _1)])
|
||||
| charset::no_case[(wdays >> dash >> wdays)] [(bind(&WeekdayRange::SetStart, _val, _1),
|
||||
bind(&WeekdayRange::SetEnd, _val, _2))]
|
||||
| charset::no_case[wdays] [bind(&WeekdayRange::SetStart, _val, _1)]
|
||||
;
|
||||
|
||||
weekday_sequence %= (weekday_range % ',') >> !qi::no_skip[charset::alpha]
|
||||
;
|
||||
|
||||
main = (holiday_sequence >> -lit(',') >> weekday_sequence)
|
||||
[(bind(&Weekdays::SetHolidays, _val, _1),
|
||||
bind(&Weekdays::SetWeekdayRanges, _val, _2))]
|
||||
| holiday_sequence [bind(&Weekdays::SetHolidays, _val, _1)]
|
||||
| weekday_sequence [bind(&Weekdays::SetWeekdayRanges, _val, _1)]
|
||||
;
|
||||
|
||||
BOOST_SPIRIT_DEBUG_NODE(main);
|
||||
BOOST_SPIRIT_DEBUG_NODE(weekday_sequence);
|
||||
BOOST_SPIRIT_DEBUG_NODE(weekday_range);
|
||||
BOOST_SPIRIT_DEBUG_NODE(holiday_sequence);
|
||||
}
|
||||
}
|
||||
|
||||
bool Parse(std::string const & str, Weekdays & context)
|
||||
{
|
||||
return osmoh::ParseImpl<parsing::weekday_selector_parser>(str, context);
|
||||
}
|
||||
} // namespace osmoh
|
||||
37
3party/opening_hours/parse_weeks.cpp
Normal file
37
3party/opening_hours/parse_weeks.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#include "parse_opening_hours.hpp"
|
||||
#include "opening_hours_parsers.hpp"
|
||||
|
||||
#include <boost/phoenix/bind.hpp>
|
||||
#include <boost/phoenix/operator.hpp> // operator,
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
namespace parsing
|
||||
{
|
||||
week_selector_parser::week_selector_parser() : week_selector_parser::base_type(main)
|
||||
{
|
||||
using qi::uint_;
|
||||
using qi::lit;
|
||||
using qi::_1;
|
||||
using qi::_2;
|
||||
using qi::_3;
|
||||
using qi::_val;
|
||||
using osmoh::WeekRange;
|
||||
|
||||
week = (weeknum >> dash >> weeknum >> '/' >> uint_) [(bind(&WeekRange::SetStart, _val, _1),
|
||||
bind(&WeekRange::SetEnd, _val, _2),
|
||||
bind(&WeekRange::SetPeriod, _val, _3))]
|
||||
| (weeknum >> dash >> weeknum) [(bind(&WeekRange::SetStart, _val, _1),
|
||||
bind(&WeekRange::SetEnd, _val, _2))]
|
||||
| weeknum [bind(&WeekRange::SetStart, _val, _1)]
|
||||
;
|
||||
|
||||
main %= charset::no_case[lit("week")] >> (week % ',');
|
||||
}
|
||||
}
|
||||
|
||||
bool Parse(std::string const & str, TWeekRanges & context)
|
||||
{
|
||||
return osmoh::ParseImpl<parsing::week_selector_parser>(str, context);
|
||||
}
|
||||
} // namespace osmoh
|
||||
41
3party/opening_hours/parse_years.cpp
Normal file
41
3party/opening_hours/parse_years.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#include "parse_opening_hours.hpp"
|
||||
#include "opening_hours_parsers.hpp"
|
||||
|
||||
#include <boost/phoenix/bind.hpp>
|
||||
#include <boost/phoenix/operator.hpp> // operator,
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
namespace parsing
|
||||
{
|
||||
year_selector_parser::year_selector_parser() : year_selector_parser::base_type(main)
|
||||
{
|
||||
using qi::uint_;
|
||||
using qi::lit;
|
||||
using qi::_1;
|
||||
using qi::_2;
|
||||
using qi::_3;
|
||||
using qi::_val;
|
||||
using osmoh::YearRange;
|
||||
|
||||
static const qi::int_parser<unsigned, 10, 4, 4> year = {};
|
||||
|
||||
year_range = (year >> dash >> year >> '/' >> uint_) [(bind(&YearRange::SetStart, _val, _1),
|
||||
bind(&YearRange::SetEnd, _val, _2),
|
||||
bind(&YearRange::SetPeriod, _val, _3))]
|
||||
| (year >> dash >> year) [(bind(&YearRange::SetStart, _val, _1),
|
||||
bind(&YearRange::SetEnd, _val, _2))]
|
||||
| (year >> lit('+')) [(bind(&YearRange::SetStart, _val, _1),
|
||||
bind(&YearRange::SetPlus, _val, true))]
|
||||
;
|
||||
|
||||
main %= (year_range % ',');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Parse(std::string const & str, TYearRanges & context)
|
||||
{
|
||||
return osmoh::ParseImpl<parsing::year_selector_parser>(str, context);
|
||||
}
|
||||
} // namespace osmoh
|
||||
522
3party/opening_hours/rules_evaluation.cpp
Normal file
522
3party/opening_hours/rules_evaluation.cpp
Normal file
|
|
@ -0,0 +1,522 @@
|
|||
#include "rules_evaluation.hpp"
|
||||
#include "rules_evaluation_private.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
|
||||
namespace
|
||||
{
|
||||
using THourMinutes = std::tuple<int, int>;
|
||||
using osmoh::operator""_h;
|
||||
|
||||
constexpr osmoh::MonthDay::TYear kTMYearOrigin = 1900;
|
||||
|
||||
bool ToHourMinutes(osmoh::Time const & t, THourMinutes & hm)
|
||||
{
|
||||
if (!t.IsHoursMinutes())
|
||||
return false;
|
||||
hm = THourMinutes{t.GetHoursCount(), t.GetMinutesCount()};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ToHourMinutes(std::tm const & t, THourMinutes & hm)
|
||||
{
|
||||
hm = THourMinutes{t.tm_hour, t.tm_min};
|
||||
return true;
|
||||
}
|
||||
|
||||
int CompareMonthDayTimeTuple(osmoh::MonthDay const & monthDay, std::tm const & date)
|
||||
{
|
||||
if (monthDay.HasYear())
|
||||
{
|
||||
if (monthDay.GetYear() != date.tm_year + kTMYearOrigin)
|
||||
return static_cast<int>(monthDay.GetYear()) - (date.tm_year + kTMYearOrigin);
|
||||
}
|
||||
|
||||
if (monthDay.HasMonth())
|
||||
{
|
||||
if (monthDay.GetMonth() != osmoh::ToMonth(date.tm_mon + 1))
|
||||
return static_cast<int>(monthDay.GetMonth()) - (date.tm_mon + 1);
|
||||
}
|
||||
|
||||
if (monthDay.HasDayNum())
|
||||
{
|
||||
if (monthDay.GetDayNum() != date.tm_mday)
|
||||
return static_cast<int>(monthDay.GetDayNum()) - date.tm_mday;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool operator<=(osmoh::MonthDay const & monthDay, std::tm const & date)
|
||||
{
|
||||
return CompareMonthDayTimeTuple(monthDay, date) < 1;
|
||||
}
|
||||
|
||||
bool operator<=(std::tm const & date, osmoh::MonthDay const & monthDay)
|
||||
{
|
||||
return CompareMonthDayTimeTuple(monthDay, date) > -1;
|
||||
}
|
||||
|
||||
bool operator==(osmoh::MonthDay const & monthDay, std::tm const & date)
|
||||
{
|
||||
return CompareMonthDayTimeTuple(monthDay, date) == 0;
|
||||
}
|
||||
|
||||
// Fill result with fields that present in start and missing in end.
|
||||
// 2015 Jan 30 - Feb 20 <=> 2015 Jan 30 - 2015 Feb 20
|
||||
// 2015 Jan 01 - 22 <=> 2015 Jan 01 - 2015 Jan 22
|
||||
osmoh::MonthDay NormalizeEnd(osmoh::MonthDay const & start, osmoh::MonthDay const & end)
|
||||
{
|
||||
osmoh::MonthDay result = start;
|
||||
if (end.HasYear())
|
||||
result.SetYear(end.GetYear());
|
||||
if (end.HasMonth())
|
||||
result.SetMonth(end.GetMonth());
|
||||
if (end.HasDayNum())
|
||||
result.SetDayNum(end.GetDayNum());
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t GetWeekNumber(std::tm const & date)
|
||||
{
|
||||
char buff[4]{};
|
||||
if (strftime(&buff[0], sizeof(buff), "%V", &date) == 0)
|
||||
return 0;
|
||||
|
||||
uint32_t weekNumber;
|
||||
std::stringstream sstr(buff);
|
||||
sstr >> weekNumber;
|
||||
return weekNumber;
|
||||
}
|
||||
|
||||
osmoh::RuleState ModifierToRuleState(osmoh::RuleSequence::Modifier const modifier)
|
||||
{
|
||||
using Modifier = osmoh::RuleSequence::Modifier;
|
||||
|
||||
switch(modifier)
|
||||
{
|
||||
case Modifier::DefaultOpen:
|
||||
case Modifier::Open:
|
||||
return osmoh::RuleState::Open;
|
||||
|
||||
case Modifier::Closed:
|
||||
return osmoh::RuleState::Closed;
|
||||
|
||||
case Modifier::Unknown:
|
||||
case Modifier::Comment:
|
||||
return osmoh::RuleState::Unknown;
|
||||
}
|
||||
std::cerr << "Unreachable\n";
|
||||
std::abort();
|
||||
return osmoh::RuleState::Unknown;
|
||||
}
|
||||
|
||||
// Transform timspan with extended end of the form of
|
||||
// time less than 24 hours to extended form, i.e from 25 to 48 hours.
|
||||
// Example: 12:15-06:00 -> 12:15-30:00.
|
||||
void NormalizeExtendedEnd(osmoh::Timespan & span)
|
||||
{
|
||||
auto & endHourMinutes = span.GetEnd().GetHourMinutes();
|
||||
auto const duration = endHourMinutes.GetDuration();
|
||||
if (duration < 24_h)
|
||||
endHourMinutes.SetDuration(duration + 24_h);
|
||||
}
|
||||
|
||||
osmoh::TTimespans SplitExtendedHours(osmoh::Timespan span)
|
||||
{
|
||||
osmoh::TTimespans result;
|
||||
NormalizeExtendedEnd(span);
|
||||
|
||||
auto spanToBeSplit = span;
|
||||
if (spanToBeSplit.HasExtendedHours())
|
||||
{
|
||||
osmoh::Timespan normalSpan;
|
||||
normalSpan.SetStart(spanToBeSplit.GetStart());
|
||||
normalSpan.SetEnd(osmoh::HourMinutes(24_h));
|
||||
result.push_back(normalSpan);
|
||||
|
||||
spanToBeSplit.SetStart(osmoh::HourMinutes(0_h));
|
||||
spanToBeSplit.GetEnd().AddDuration(-24_h);
|
||||
}
|
||||
result.push_back(spanToBeSplit);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Spans can be of three different types:
|
||||
// 1. Normal span - start time is less then end time and end time is less then 24h. Spans of this
|
||||
// type will be added into |originalNormalizedSpans| as is, |additionalSpan| will be empty.
|
||||
// 2. Extended span - start time is greater or equal to end time and end time is not equal to
|
||||
// 00:00 (for ex. 08:00-08:00 or 08:00-03:00), this span will be split into two normal spans
|
||||
// first will be added into |originalNormalizedSpans| and second will be saved into
|
||||
// |additionalSpan|. We don't handle more than one occurence of extended span since it is an
|
||||
// invalid situation.
|
||||
// 3. Special case - end time is equal to 00:00 (for ex. 08:00-00:00), span of this type will be
|
||||
// normalized and added into |originalNormalizedSpans|, |additionalSpan| will be empty.
|
||||
//
|
||||
// TODO(mgsergio): interpret 00:00 at the end of the span as normal, not extended hours.
|
||||
void SplitExtendedHours(osmoh::TTimespans const & spans,
|
||||
osmoh::TTimespans & originalNormalizedSpans,
|
||||
osmoh::Timespan & additionalSpan)
|
||||
{
|
||||
originalNormalizedSpans.clear();
|
||||
additionalSpan = {};
|
||||
|
||||
auto it = begin(spans);
|
||||
for (; it != end(spans) && !it->HasExtendedHours(); ++it)
|
||||
originalNormalizedSpans.push_back(*it);
|
||||
|
||||
if (it == end(spans))
|
||||
return;
|
||||
|
||||
auto const splittedSpans = SplitExtendedHours(*it);
|
||||
originalNormalizedSpans.push_back(splittedSpans[0]);
|
||||
// if a span remains extended after normalization, then it will be split into two different spans.
|
||||
if (splittedSpans.size() > 1)
|
||||
additionalSpan = splittedSpans[1];
|
||||
|
||||
++it;
|
||||
std::copy(it, end(spans), back_inserter(originalNormalizedSpans));
|
||||
}
|
||||
|
||||
bool HasExtendedHours(osmoh::RuleSequence const & rule)
|
||||
{
|
||||
for (auto const & timespan : rule.GetTimes())
|
||||
{
|
||||
if (timespan.HasExtendedHours())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::tm MakeTimetuple(time_t const timestamp)
|
||||
{
|
||||
std::tm tm{};
|
||||
localtime_r(×tamp, &tm);
|
||||
return tm;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
// ADL shadows ::operator==.
|
||||
using ::operator==;
|
||||
|
||||
bool IsActive(Timespan span, std::tm const & time)
|
||||
{
|
||||
// Timespan with e.h. should be split into parts with no e.h.
|
||||
// before calling IsActive().
|
||||
// TODO(mgsergio): set assert(!span.HasExtendedHours())
|
||||
|
||||
span.ExpandPlus();
|
||||
if (span.HasStart() && span.HasEnd())
|
||||
{
|
||||
THourMinutes start;
|
||||
THourMinutes end;
|
||||
THourMinutes toBeChecked;
|
||||
|
||||
if (!ToHourMinutes(span.GetStart(), start))
|
||||
return false;
|
||||
|
||||
if (!ToHourMinutes(span.GetEnd(), end))
|
||||
return false;
|
||||
|
||||
if (!ToHourMinutes(time, toBeChecked))
|
||||
return false;
|
||||
|
||||
return start <= toBeChecked && toBeChecked <= end;
|
||||
}
|
||||
else if (span.HasStart() && span.HasPlus())
|
||||
{
|
||||
// TODO(mgsergio): Not implemented yet
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsActive(WeekdayRange const & range, std::tm const & date)
|
||||
{
|
||||
if (range.IsEmpty())
|
||||
return false;
|
||||
|
||||
auto const wday = ToWeekday(date.tm_wday + 1);
|
||||
if (wday == Weekday::None)
|
||||
return false;
|
||||
|
||||
return range.HasWday(wday);
|
||||
}
|
||||
|
||||
bool IsActive(Holiday const & holiday, std::tm const & date)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsActive(Weekdays const & weekdays, std::tm const & date)
|
||||
{
|
||||
for (auto const & wr : weekdays.GetWeekdayRanges())
|
||||
if (IsActive(wr, date))
|
||||
return true;
|
||||
|
||||
for (auto const & hd : weekdays.GetHolidays())
|
||||
if (IsActive(hd, date))
|
||||
return true;
|
||||
|
||||
return weekdays.GetWeekdayRanges().empty() &&
|
||||
weekdays.GetHolidays().empty();
|
||||
}
|
||||
|
||||
bool IsActive(MonthdayRange const & range, std::tm const & date)
|
||||
{
|
||||
if (range.IsEmpty())
|
||||
return false;
|
||||
|
||||
if (range.HasEnd())
|
||||
{
|
||||
auto const & start = range.GetStart();
|
||||
auto const end = NormalizeEnd(range.GetStart(), range.GetEnd());
|
||||
if (start <= end)
|
||||
return start <= date && date <= end;
|
||||
else
|
||||
return start <= date || date <= end;
|
||||
}
|
||||
|
||||
return range.GetStart() == date;
|
||||
}
|
||||
|
||||
bool IsActive(YearRange const & range, std::tm const & date)
|
||||
{
|
||||
if (range.IsEmpty())
|
||||
return false;
|
||||
|
||||
auto const year = date.tm_year + kTMYearOrigin;
|
||||
|
||||
if (range.HasEnd())
|
||||
return range.GetStart() <= year && year <= range.GetEnd();
|
||||
|
||||
return range.GetStart() == year;
|
||||
}
|
||||
|
||||
bool IsActive(WeekRange const & range, std::tm const & date)
|
||||
{
|
||||
if (range.IsEmpty())
|
||||
return false;
|
||||
|
||||
auto const weekNumber = GetWeekNumber(date);
|
||||
|
||||
if (range.HasEnd())
|
||||
return range.GetStart() <= weekNumber && weekNumber <= range.GetEnd();
|
||||
|
||||
return range.GetStart() == weekNumber;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool IsActiveAny(std::vector<T> const & selectors, std::tm const & date)
|
||||
{
|
||||
for (auto const & selector : selectors)
|
||||
{
|
||||
if (IsActive(selector, date))
|
||||
return true;
|
||||
}
|
||||
|
||||
return selectors.empty();
|
||||
}
|
||||
|
||||
bool IsActiveAny(Timespan const & span, std::tm const & time) { return IsActive(span, time); }
|
||||
|
||||
bool IsDayActive(RuleSequence const & rule, std::tm const & dt)
|
||||
{
|
||||
return IsActiveAny(rule.GetYears(), dt) && IsActiveAny(rule.GetMonths(), dt) &&
|
||||
IsActiveAny(rule.GetWeeks(), dt) && IsActive(rule.GetWeekdays(), dt);
|
||||
}
|
||||
|
||||
template <class TTimeSpans>
|
||||
std::pair<bool, bool> MakeActiveResult(RuleSequence const & rule, std::tm const & dt, TTimeSpans const & times)
|
||||
{
|
||||
if (IsDayActive(rule, dt))
|
||||
return { true, IsActiveAny(times, dt) };
|
||||
else
|
||||
return {false, false};
|
||||
}
|
||||
|
||||
/// @return [day active, time active].
|
||||
std::pair<bool, bool> IsActiveImpl(RuleSequence const & rule, time_t const timestamp)
|
||||
{
|
||||
if (rule.IsTwentyFourHours())
|
||||
return {true, true};
|
||||
|
||||
auto const dateTimeTM = MakeTimetuple(timestamp);
|
||||
if (!HasExtendedHours(rule))
|
||||
return MakeActiveResult(rule, dateTimeTM, rule.GetTimes());
|
||||
|
||||
TTimespans originalNormalizedSpans;
|
||||
Timespan additionalSpan;
|
||||
SplitExtendedHours(rule.GetTimes(), originalNormalizedSpans, additionalSpan);
|
||||
|
||||
auto const res1 = MakeActiveResult(rule, dateTimeTM, originalNormalizedSpans);
|
||||
if (res1.first && res1.second)
|
||||
return res1;
|
||||
|
||||
time_t constexpr twentyFourHoursShift = 24 * 60 * 60;
|
||||
auto const dateTimeTMShifted = MakeTimetuple(timestamp - twentyFourHoursShift);
|
||||
|
||||
auto const res2 = MakeActiveResult(rule, dateTimeTMShifted, additionalSpan);
|
||||
return { res1.first || res2.first, res2.second };
|
||||
}
|
||||
|
||||
/// Check if r1 is more general range and includes r2.
|
||||
bool IsR1IncludesR2(RuleSequence const & r1, RuleSequence const & r2)
|
||||
{
|
||||
/// @todo Very naive implementation, but ok in most cases.
|
||||
if ((r1.GetYears().empty() && !r2.GetYears().empty()) ||
|
||||
(r1.GetMonths().empty() && !r2.GetMonths().empty()) ||
|
||||
(r1.GetWeeks().empty() && !r2.GetWeeks().empty()) ||
|
||||
(r1.GetWeekdays().IsEmpty() && !r2.GetWeekdays().IsEmpty()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsActive(RuleSequence const & rule, time_t const timestamp)
|
||||
{
|
||||
auto const res = IsActiveImpl(rule, timestamp);
|
||||
return res.first && res.second;
|
||||
}
|
||||
|
||||
time_t GetNextTimeState(TRuleSequences const & rules, time_t const dateTime, RuleState state)
|
||||
{
|
||||
time_t constexpr kTimeTMax = std::numeric_limits<time_t>::max();
|
||||
time_t dateTimeResult = kTimeTMax;
|
||||
time_t dateTimeToCheck;
|
||||
|
||||
// Check in the next 7 days
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
for (auto it = rules.rbegin(); it != rules.rend(); ++it)
|
||||
{
|
||||
auto const & times = it->GetTimes();
|
||||
|
||||
// If the rule has no times specified, check at 00:00
|
||||
if (times.size() == 0)
|
||||
{
|
||||
tm tm = MakeTimetuple(dateTime);
|
||||
tm.tm_hour = 0;
|
||||
tm.tm_min = 0;
|
||||
dateTimeToCheck = mktime(&tm);
|
||||
if (dateTimeToCheck == -1)
|
||||
continue;
|
||||
dateTimeToCheck += i * (24 * 60 * 60);
|
||||
|
||||
if (dateTimeToCheck < dateTime || dateTimeToCheck > dateTimeResult)
|
||||
continue;
|
||||
|
||||
if (GetState(rules, dateTimeToCheck) == state)
|
||||
dateTimeResult = dateTimeToCheck;
|
||||
}
|
||||
|
||||
if ((state == RuleState::Open && it->GetModifier() == RuleSequence::Modifier::Closed) ||
|
||||
(state == RuleState::Closed &&
|
||||
(it->GetModifier() == RuleSequence::Modifier::Open || it->GetModifier() == RuleSequence::Modifier::DefaultOpen)))
|
||||
{
|
||||
// Check the ending time of each rule
|
||||
for (auto const & time : times)
|
||||
{
|
||||
tm tm = MakeTimetuple(dateTime);
|
||||
tm.tm_hour = time.GetEnd().GetHourMinutes().GetHoursCount();
|
||||
tm.tm_min = time.GetEnd().GetHourMinutes().GetMinutesCount();
|
||||
dateTimeToCheck = mktime(&tm);
|
||||
if (dateTimeToCheck == -1)
|
||||
continue;
|
||||
dateTimeToCheck += i * (24 * 60 * 60) + 60; // 1 minute offset
|
||||
|
||||
if (dateTimeToCheck < dateTime || dateTimeToCheck > dateTimeResult)
|
||||
continue;
|
||||
|
||||
if (GetState(rules, dateTimeToCheck) == state)
|
||||
dateTimeResult = dateTimeToCheck - 60; // remove 1 minute offset
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check the starting time of each rule
|
||||
for (auto const & time : times)
|
||||
{
|
||||
tm tm = MakeTimetuple(dateTime);
|
||||
tm.tm_hour = time.GetStart().GetHourMinutes().GetHoursCount();
|
||||
tm.tm_min = time.GetStart().GetHourMinutes().GetMinutesCount();
|
||||
dateTimeToCheck = mktime(&tm);
|
||||
if (dateTimeToCheck == -1)
|
||||
continue;
|
||||
dateTimeToCheck += i * (24 * 60 * 60);
|
||||
|
||||
if (dateTimeToCheck < dateTime || dateTimeToCheck > dateTimeResult)
|
||||
continue;
|
||||
|
||||
if (GetState(rules, dateTimeToCheck) == state)
|
||||
dateTimeResult = dateTimeToCheck;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dateTimeResult < kTimeTMax)
|
||||
return dateTimeResult;
|
||||
}
|
||||
|
||||
return kTimeTMax;
|
||||
}
|
||||
|
||||
RuleState GetState(TRuleSequences const & rules, time_t const timestamp)
|
||||
{
|
||||
RuleSequence const * emptyRule = nullptr;
|
||||
RuleSequence const * dayMatchedRule = nullptr;
|
||||
|
||||
for (auto it = rules.rbegin(); it != rules.rend(); ++it)
|
||||
{
|
||||
auto const & rule = *it;
|
||||
auto const res = IsActiveImpl(rule, timestamp);
|
||||
if (!res.first)
|
||||
continue;
|
||||
|
||||
bool const empty = rule.IsEmpty();
|
||||
if (res.second)
|
||||
{
|
||||
if (empty && !emptyRule)
|
||||
emptyRule = &rule;
|
||||
else
|
||||
{
|
||||
// Should understand if previous 'rule vs dayMatchedRule' should work
|
||||
// like 'general x BUT specific y' or like 'x OR y'.
|
||||
|
||||
if (dayMatchedRule && IsR1IncludesR2(rule, *dayMatchedRule))
|
||||
return RuleState::Closed;
|
||||
|
||||
return ModifierToRuleState(rule.GetModifier());
|
||||
}
|
||||
}
|
||||
else if (!empty && !dayMatchedRule && ModifierToRuleState(rule.GetModifier()) == RuleState::Open)
|
||||
{
|
||||
// Save recent day-matched rule with Open state, but not time-matched.
|
||||
dayMatchedRule = &rule;
|
||||
}
|
||||
}
|
||||
|
||||
if (emptyRule)
|
||||
{
|
||||
if (emptyRule->HasComment())
|
||||
return RuleState::Unknown;
|
||||
else
|
||||
return ModifierToRuleState(emptyRule->GetModifier());
|
||||
}
|
||||
|
||||
return (rules.empty()
|
||||
? RuleState::Unknown
|
||||
: RuleState::Closed);
|
||||
}
|
||||
} // namespace osmoh
|
||||
43
3party/opening_hours/rules_evaluation.hpp
Normal file
43
3party/opening_hours/rules_evaluation.hpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "opening_hours.hpp"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
|
||||
RuleState GetState(TRuleSequences const & rules, time_t const dateTime);
|
||||
|
||||
time_t GetNextTimeState(TRuleSequences const & rules, time_t const dateTime, RuleState state);
|
||||
|
||||
inline bool IsOpen(TRuleSequences const & rules, time_t const dateTime)
|
||||
{
|
||||
return GetState(rules, dateTime) == RuleState::Open;
|
||||
}
|
||||
|
||||
inline time_t GetNextTimeOpen(TRuleSequences const & rules, time_t const dateTime)
|
||||
{
|
||||
if (GetState(rules, dateTime) == RuleState::Open)
|
||||
return dateTime;
|
||||
return GetNextTimeState(rules, dateTime, RuleState::Open);
|
||||
}
|
||||
|
||||
inline bool IsClosed(TRuleSequences const & rules, time_t const dateTime)
|
||||
{
|
||||
return GetState(rules, dateTime) == RuleState::Closed;
|
||||
}
|
||||
|
||||
inline time_t GetNextTimeClosed(TRuleSequences const & rules, time_t const dateTime)
|
||||
{
|
||||
if (GetState(rules, dateTime) == RuleState::Closed)
|
||||
return dateTime;
|
||||
return GetNextTimeState(rules, dateTime, RuleState::Closed);
|
||||
}
|
||||
|
||||
inline bool IsUnknown(TRuleSequences const & rules, time_t const dateTime)
|
||||
{
|
||||
return GetState(rules, dateTime) == RuleState::Unknown;
|
||||
}
|
||||
|
||||
} // namespace osmoh
|
||||
17
3party/opening_hours/rules_evaluation_private.hpp
Normal file
17
3party/opening_hours/rules_evaluation_private.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "opening_hours.hpp"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
namespace osmoh
|
||||
{
|
||||
bool IsActive(Timespan span, std::tm const & date);
|
||||
bool IsActive(WeekdayRange const & range, std::tm const & date);
|
||||
bool IsActive(Holiday const & holiday, std::tm const & date);
|
||||
bool IsActive(Weekdays const & weekdays, std::tm const & date);
|
||||
bool IsActive(MonthdayRange const & range, std::tm const & date);
|
||||
bool IsActive(YearRange const & range, std::tm const & date);
|
||||
bool IsActive(WeekRange const & range, std::tm const & date);
|
||||
bool IsActive(RuleSequence const & rule, time_t const timestamp);
|
||||
} // namespace osmoh
|
||||
Loading…
Add table
Add a link
Reference in a new issue