Merge pull request #358 from mgsergio/fix-old-parser

opening_hours parser reloading
This commit is contained in:
Viktor Govako 2015-11-12 15:42:49 +03:00
commit 2f2ffe130a
31 changed files with 161730 additions and 2016 deletions

View file

@ -2,16 +2,30 @@
TEMPLATE = subdirs
SUBDIRS = freetype fribidi minizip jansson tomcrypt protobuf osrm expat \
succinct \
!linux* {
SUBDIRS += opening_hours \
}
# TODO: Avoid duplication here and in omim.pro by moving opening_hours tests out of 3party.
!iphone*:!tizen*:!android* {
SUBDIRS += gflags \
libtess2 \
gmock \
CONFIG *= desktop
}
SUBDIRS = freetype fribidi minizip jansson tomcrypt protobuf osrm expat succinct
# TODO(mgsrergio): Move opening hours out of 3party to the main project tree.
# See https://trello.com/c/tWYSnXSS/22-opening-hours-3party-boost-test-framework.
SUBDIRS *= opening_hours
CONFIG(desktop):!CONFIG(no-tests) {
opening_hours_tests.subdir = opening_hours/opening_hours_tests
opening_hours_tests.depends = opening_hours
SUBDIRS *= opening_hours_tests
opening_hours_integration_tests.subdir = opening_hours/opening_hours_integration_tests
opening_hours_integration_tests.depends = opening_hours
SUBDIRS *= opening_hours_integration_tests
opening_hours_supported_features_tests.subdir = opening_hours/opening_hours_supported_features_tests
opening_hours_supported_features_tests.depends = opening_hours
SUBDIRS *= opening_hours_supported_features_tests
}
CONFIG(desktop) {
SUBDIRS *= gflags libtess2 gmock
}

View file

@ -545,7 +545,7 @@ namespace boost { namespace proto
BOOST_PROTO_USE_GET_POINTER();
typedef typename detail::class_member_traits<function_type>::class_type class_type;
return (
BOOST_PROTO_GET_POINTER(class_type, (BOOST_PROTO_DEFAULT_EVAL(~, 1, e))) ->*
BOOST_PROTO_GET_POINTER(class_type, (BOOST_PROTO_DEFAULT_EVAL(~, 1, e))) ->*
BOOST_PROTO_DEFAULT_EVAL(~, 0, e)
)();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,656 @@
/*
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>
namespace osmoh
{
class HourMinutes
{
public:
using THours = std::chrono::hours;
using TMinutes = std::chrono::minutes;
HourMinutes() = default;
explicit HourMinutes(THours const duration);
explicit HourMinutes(TMinutes const duration);
bool IsEmpty() const;
THours GetHours() const;
TMinutes GetMinutes() const;
TMinutes GetDuration() const;
THours::rep GetHoursCount() const;
TMinutes::rep GetMinutesCount() const;
TMinutes::rep GetDurationCount() const;
void SetHours(THours const hours);
void SetMinutes(TMinutes const minutes);
void SetDuration(TMinutes const duration);
void AddDuration(TMinutes const 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);
class Time;
class TimeEvent
{
public:
enum class Event
{
None,
Sunrise,
Sunset
};
TimeEvent() = default;
TimeEvent(Event const event);
bool IsEmpty() const;
bool HasOffset() const;
Event GetEvent() const;
void SetEvent(Event const event);
HourMinutes const & GetOffset() const;
void SetOffset(HourMinutes const & offset);
void AddDurationToOffset(HourMinutes::TMinutes const 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);
Time(TimeEvent const & te);
Type GetType() const;
THours::rep GetHoursCount() const;
TMinutes::rep GetMinutesCount() const;
THours GetHours() const;
TMinutes GetMinutes() const;
void AddDuration(TMinutes const duration);
TimeEvent const & GetEvent() const;
void SetEvent(TimeEvent const & event);
HourMinutes const & GetHourMinutes() const;
void SetHourMinutes(HourMinutes const & hm);
bool IsEmpty() const;
bool IsTime() const;
bool IsEvent() const;
bool IsHoursMinutes() const;
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);
class TimespanPeriod
{
public:
enum class Type
{
None,
Minutes,
HourMinutes
};
TimespanPeriod() = default;
TimespanPeriod(HourMinutes const & hm);
TimespanPeriod(HourMinutes::TMinutes const minutes);
bool IsEmpty() const;
bool IsHoursMinutes() const;
bool IsMinutes() const;
HourMinutes const & GetHourMinutes() const;
HourMinutes::TMinutes GetMinutes() const;
HourMinutes::TMinutes::rep GetMinutesCount() const;
private:
HourMinutes::TMinutes m_minutes;
HourMinutes m_hourMinutes;
Type m_type = Type::None;
};
std::ostream & operator<<(std::ostream & ost, TimespanPeriod const p);
class Timespan
{
public:
bool IsEmpty() const;
bool IsOpen() const;
bool HasStart() const;
bool HasEnd() const;
bool HasPlus() const;
bool HasPeriod() const;
Time const & GetStart() const;
Time const & GetEnd() const;
TimespanPeriod const & GetPeriod() const;
void SetStart(Time const & start);
void SetEnd(Time const & end);
void SetPeriod(TimespanPeriod const & period);
void SetPlus(bool const plus);
bool IsValid() const;
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);
class NthWeekdayOfTheMonthEntry
{
public:
enum class NthDayOfTheMonth
{
None,
First,
Second,
Third,
Fourth,
Fifth
};
bool IsEmpty() const;
bool HasStart() const;
bool HasEnd() const;
NthDayOfTheMonth GetStart() const;
NthDayOfTheMonth GetEnd() const;
void SetStart(NthDayOfTheMonth const s);
void SetEnd(NthDayOfTheMonth const e);
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 const 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 const wday);
class WeekdayRange
{
using TNths = std::vector<NthWeekdayOfTheMonthEntry>;
public:
bool HasWday(Weekday const & wday) const;
bool HasSunday() const;
bool HasMonday() const;
bool HasTuesday() const;
bool HasWednesday() const;
bool HasThursday() const;
bool HasFriday() const;
bool HasSaturday() const;
bool HasStart() const;
bool HasEnd() const;
bool HasOffset() const;
bool IsEmpty() const;
Weekday GetStart() const;
Weekday GetEnd() const;
void SetStart(Weekday const & wday);
void SetEnd(Weekday const & wday);
int32_t GetOffset() const;
void SetOffset(int32_t const offset);
bool HasNth() const;
TNths const & GetNths() const;
void AddNth(NthWeekdayOfTheMonthEntry const & entry);
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;
void SetPlural(bool const plural);
int32_t GetOffset() const;
void SetOffset(int32_t const offset);
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;
bool HasWeekday() const;
bool HasHolidays() const;
TWeekdayRanges const & GetWeekdayRanges() const;
THolidays const & GetHolidays() const;
void SetWeekdayRanges(TWeekdayRanges const ranges);
void SetHolidays(THolidays const & holidays);
void AddWeekdayRange(WeekdayRange const range);
void AddHoliday(Holiday const & holiday);
private:
TWeekdayRanges m_weekdayRanges;
THolidays m_holidays;
};
std::ostream & operator<<(std::ostream & ost, Weekdays const & weekday);
class DateOffset
{
public:
bool IsEmpty() const;
bool HasWDayOffset() const;
bool HasOffset() const;
bool IsWDayOffsetPositive() const;
Weekday GetWDayOffset() const;
int32_t GetOffset() const;
void SetWDayOffset(Weekday const wday);
void SetOffset(int32_t const offset);
void SetWDayOffsetPositive(bool const on);
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;
bool IsVariable() const;
bool HasYear() const;
bool HasMonth() const;
bool HasDayNum() const;
bool HasOffset() const;
TYear GetYear() const;
Month GetMonth() const;
TDayNum GetDayNum() const;
DateOffset const & GetOffset() const;
VariableDate GetVariableDate() const;
void SetYear(TYear const year);
void SetMonth(Month const month);
void SetDayNum(TDayNum const daynum);
void SetOffset(DateOffset const & offset);
void SetVariableDate(VariableDate const date);
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 const 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;
bool HasStart() const;
bool HasEnd() const;
bool HasPeriod() const;
bool HasPlus() const;
MonthDay const & GetStart() const;
MonthDay const & GetEnd() const;
uint32_t GetPeriod() const;
void SetStart(MonthDay const & start);
void SetEnd(MonthDay const & end);
void SetPeriod(uint32_t const period);
void SetPlus(bool const plus);
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;
bool IsOpen() const;
bool HasStart() const;
bool HasEnd() const;
bool HasPlus() const;
bool HasPeriod() const;
TYear GetStart() const;
TYear GetEnd() const;
uint32_t GetPeriod() const;
void SetStart(TYear const start);
void SetEnd(TYear const end);
void SetPlus(bool const plus);
void SetPeriod(uint32_t const period);
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;
bool IsOpen() const;
bool HasStart() const;
bool HasEnd() const;
bool HasPeriod() const;
TWeek GetStart() const;
TWeek GetEnd() const;
uint32_t GetPeriod() const;
void SetStart(TWeek const start);
void SetEnd(TWeek const end);
void SetPeriod(uint32_t const period);
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;
bool IsTwentyFourHours() const;
bool HasYears() const;
bool HasMonths() const;
bool HasWeeks() const;
bool HasWeekdays() const;
bool HasTimes() const;
bool HasComment() const;
bool HasModifierComment() const;
bool HasSeparatorForReadability() const;
TYearRanges const & GetYears() const;
TMonthdayRanges const & GetMonths() const;
TWeekRanges const & GetWeeks() const;
Weekdays const & GetWeekdays() const;
TTimespans const & GetTimes() const;
std::string const & GetComment() const;
std::string const & GetModifierComment() const;
std::string const & GetAnySeparator() const;
Modifier GetModifier() const;
void SetTwentyFourHours(bool const on);
void SetYears(TYearRanges const & years);
void SetMonths(TMonthdayRanges const & months);
void SetWeeks(TWeekRanges const & weeks);
void SetWeekdays(Weekdays const & weekdays);
void SetTimes(TTimespans const & times);
void SetComment(std::string const & comment);
void SetModifierComment(std::string & comment);
void SetAnySeparator(std::string const & separator);
void SetSeparatorForReadability(bool const on);
void SetModifier(Modifier const modifier);
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);
class OpeningHours
{
public:
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;
bool IsValid() const;
private:
TRuleSequences m_rule;
bool const m_valid;
};
} // namespace osmoh

View file

@ -4,14 +4,22 @@
#
#-------------------------------------------------
ROOT_DIR = ../..
TARGET = opening_hours
TEMPLATE = lib
CONFIG += staticlib
ROOT_DIR = ../..
include($$ROOT_DIR/common.pri)
SOURCES += osm_time_range.cpp
HEADERS += opening_hours.hpp \
opening_hours_parsers.hpp \
opening_hours_parsers_terminals.hpp \
parse_opening_hours.hpp \
rules_evaluation_private.hpp \
rules_evaluation.hpp
HEADERS += osm_time_range.hpp
SOURCES += rules_evaluation.cpp \
opening_hours.cpp \
parse_opening_hours.cpp

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,221 @@
#include "parse_opening_hours.hpp"
#include "rules_evaluation.hpp"
#define BOOST_TEST_MODULE OpeningHoursIntegration
#include <algorithm>
#include <ctime>
#include <map>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/test/included/unit_test.hpp>
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 HasExtendedHours(osmoh::TTimespans const & spans)
{
auto const hasExtendedHours = [](osmoh::Timespan const & s) -> bool
{
if (!s.HasEnd())
return false;
auto const startDuration = s.GetStart().GetMinutes() + s.GetStart().GetHours();
auto const endDuration = s.GetEnd().GetMinutes() + s.GetEnd().GetHours();
return endDuration > 24 * std::chrono::minutes(60) || startDuration > endDuration;
};
return std::any_of(begin(spans), end(spans), hasExtendedHours);
}
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,
// True if a rule has Timespen with more than 24 hours at the end
// or and greater than start.
// Example:
// 12:00-29:00
// 13:12-06:15
ExtendedHours,
Offset,
Count_
};
using TRuleFeatures = std::array<bool, Count_>;
std::ostream & operator<<(std::ostream & ost, TRuleFeatures const & f)
{
std::copy(begin(f), 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());
features[ExtendedHours] |= HasExtendedHours(r.GetTimes());
}
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\tExtendedHours\tOffset\tCount" << std::endl;
for (auto const & e : featuresDistrib)
message << e.first << '\t' << e.second << std::endl;
BOOST_TEST_MESSAGE(message.str());
}
}

View file

@ -0,0 +1,19 @@
TARGET = opening_hours_integration_tests
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
ROOT_DIR = ../../..
DEPENDENCIES += opening_hours \
include($$ROOT_DIR/common.pri)
OPENING_HOURS_INCLUDE = $$ROOT_DIR/3party/opening_hours
INCLUDEPATH += $$OPENING_HOURS_INCLUDE
HEADERS += $$OPENING_HOURS_INCLUDE/opening_hours.hpp \
$$OPENING_HOURS_INCLUDE/parse_opening_hours.hpp \
$$OPENING_HOURS_INCLUDE/rules_evaluation.hpp \
$$OPENING_HOURS_INCLUDE/rules_evaluation_private.hpp
SOURCES += opening_hours_integration_tests.cpp

View file

@ -0,0 +1,488 @@
#pragma once
#include "opening_hours.hpp"
// #define BOOST_SPIRIT_DEBUG
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/qi.hpp>
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#pragma clang diagnostic pop
#else
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#endif
#include "opening_hours_parsers_terminals.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;
template <class Iterator>
class year_selector : 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() : year_selector::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)]
| year [bind(&YearRange::SetStart, _val, _1)]
;
main %= (year_range % ',');
}
};
template <typename Iterator>
class week_selector : 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() : week_selector::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 % ',');
}
};
template <typename Iterator>
class month_selector : 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() : month_selector::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);
}
};
template <typename Iterator>
class weekday_selector : 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() : weekday_selector::base_type(main)
{
using qi::_a;
using qi::_1;
using qi::_2;
using qi::_val;
using qi::lit;
using qi::ushort_;
using boost::phoenix::bind;
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);
}
};
template <typename Iterator>
class time_selector : 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() : time_selector::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 boost::phoenix::bind;
using boost::phoenix::construct;
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)]
| 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);
}
};
template <typename Iterator>
class time_domain : public qi::grammar<Iterator, osmoh::TRuleSequences(), space_type>
{
protected:
weekday_selector<Iterator> weekday_selector;
time_selector<Iterator> time_selector;
year_selector<Iterator> year_selector;
month_selector<Iterator> month_selector;
week_selector<Iterator> 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 phx::construct;
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")]
[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
} // namespace osmoh
#undef BOOST_SPIRIT_USE_PHOENIX_V3

View file

@ -0,0 +1,143 @@
// This header file should be used only with opening_hours_parsers.hpp.
// It's only purpose is to avoid polution opening_hours_parsers.hpp with
// it's content.
namespace osmoh
{
namespace parsing
{
namespace qi = boost::spirit::qi;
struct dash_ : public qi::symbols<char>
{
dash_()
{
add
("-")
/* not standard */
// (L"")(L"—")(L"")(L"~")(L"")(L"〜")(L"to")(L"às")(L"ás")(L"as")(L"a")(L"ate")(L"bis")
;
}
} dash;
struct event_ : public qi::symbols<char, osmoh::TimeEvent::Event>
{
event_()
{
add
("dawn", osmoh::TimeEvent::Event::Sunrise)
("sunrise", osmoh::TimeEvent::Event::Sunrise)
("sunset", osmoh::TimeEvent::Event::Sunset)
("dusk", osmoh::TimeEvent::Event::Sunset)
;
}
} event;
struct wdays_ : qi::symbols<char, osmoh::Weekday>
{
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)
;
}
} wdays;
struct month_ : qi::symbols<char, osmoh::MonthDay::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)
;
}
} month;
struct hours_ : qi::symbols<char, osmoh::Time::THours>
{
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)
;
}
} hours;
struct exthours_ : qi::symbols<char, osmoh::Time::THours>
{
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)
;
}
} exthours;
struct minutes_ : qi::symbols<char, osmoh::Time::TMinutes>
{
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)
;
}
} minutes;
struct weeknum_ : qi::symbols<char, unsigned>
{
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)
;
}
} weeknum;
struct daynum_ : qi::symbols<char, MonthDay::TDayNum>
{
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)
;
}
} daynum;
} // namespace parsing
} // namespace osmoh

View file

@ -0,0 +1,19 @@
TARGET = opening_hours_supported_features_tests
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
ROOT_DIR = ../../..
DEPENDENCIES += opening_hours \
include($$ROOT_DIR/common.pri)
OPENING_HOURS_INCLUDE = $$ROOT_DIR/3party/opening_hours
INCLUDEPATH += $$OPENING_HOURS_INCLUDE
HEADERS += $$OPENING_HOURS_INCLUDE/opening_hours.hpp \
$$OPENING_HOURS_INCLUDE/parse_opening_hours.hpp \
$$OPENING_HOURS_INCLUDE/rules_evaluation.hpp \
$$OPENING_HOURS_INCLUDE/rules_evaluation_private.hpp
SOURCES += opening_hours_supported_features_tests.cpp

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,19 @@
TARGET = opening_hours_tests
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
ROOT_DIR = ../../..
DEPENDENCIES += opening_hours \
include($$ROOT_DIR/common.pri)
OPENING_HOURS_INCLUDE = $$ROOT_DIR/3party/opening_hours
INCLUDEPATH += $$OPENING_HOURS_INCLUDE
HEADERS += $$OPENING_HOURS_INCLUDE/opening_hours.hpp \
$$OPENING_HOURS_INCLUDE/parse_opening_hours.hpp \
$$OPENING_HOURS_INCLUDE/rules_evaluation.hpp \
$$OPENING_HOURS_INCLUDE/rules_evaluation_private.hpp
SOURCES += opening_hours_tests.cpp

View file

@ -1,951 +0,0 @@
/*
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 "osm_time_range.hpp"
#include <chrono>
#include <iomanip>
#include <ios>
#include <vector>
#include <codecvt>
//#define BOOST_SPIRIT_DEBUG 1
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#pragma clang diagnostic pop
#else
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#endif
namespace osmoh
{
std::ostream & operator << (std::ostream & s, Time const & t)
{
bool event = (t.flags & Time::eSunrise) || (t.flags & Time::eSunset);
if (event)
s << ((t.flags & Time::eSunrise) ? "sunrise" : "sunset") << " (";
std::ios_base::fmtflags sf = s.flags();
if (t.flags & (Time::ePlus | Time::eMinus))
s << ((t.flags & Time::ePlus) ? "+" : "-");
if (t.flags & Time::eHours)
s << std::setw(2) << std::setfill('0') << (int)t.hours;
if (t.flags & Time::eMinutes)
s << ":" << std::setw(2) << std::setfill('0') << (int)t.minutes;
s.flags(sf);
if (event)
s << ")";
return s;
}
std::ostream & operator << (std::ostream & s, TimeSpan const & span)
{
s << span.from;
if (span.to.flags)
s << '-' << span.to;
if (span.flags == Time::ePlus)
s << "...";
if (span.flags == Time::eExt)
s << '/' << span.period;
return s;
}
std::ostream & operator << (std::ostream & s, Weekdays const & w)
{
static char const * wdays[] = {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"};
static uint8_t const kDaysInWeek = 7;
static uint8_t const kWeeksInMonth = 5;
for (size_t i = 0; i < kDaysInWeek; ++i)
{
if (w.weekdays & (1 << i))
{
if (w.weekdays & ((1 << i) - 1))
s << ',';
s << wdays[i];
}
}
if (w.nth)
{
s << "[";
uint8_t a = w.nth & 0xFF;
for (size_t i = 0; i < kWeeksInMonth; ++i)
{
if (a & (1 << i))
{
if (a & ((1 << i) - 1))
s << ',';
s << (i + 1);
}
}
a = (w.nth >> 8) & 0xFF;
for (size_t i = 0; i < kWeeksInMonth; ++i)
{
if (a & (1 << i))
{
if (a & ((1 << i) - 1))
s << ',';
s << '-' << (i + 1);
}
}
s << "]";
}
if (w.offset)
s << ' ' << w.offset << " day(s)";
return s;
}
std::ostream & operator << (std::ostream & s, State const & w)
{
static char const * st[] = {"unknown", "closed", "open"};
s << ' ' << st[w.state] << " " << w.comment;
return s;
}
std::ostream & operator << (std::ostream & s, TimeRule const & w)
{
for (auto const & e : w.weekdays)
s << e;
if (!w.weekdays.empty() && !w.timespan.empty())
s << ' ';
for (auto const & e : w.timespan)
s << e;
return s << w.state;
}
boost::posix_time::time_period make_time_period(boost::gregorian::date const & d, osmoh::TimeSpan const & ts)
{
using boost::posix_time::ptime;
using boost::posix_time::hours;
using boost::posix_time::minutes;
using boost::posix_time::time_period;
/// TODO(yershov@): Need create code for calculate real values
ptime sunrise(d, hours(6));
ptime sunset(d, hours(19));
ptime t1, t2;
if (ts.from.flags & osmoh::Time::eSunrise)
t1 = sunrise;
else if (ts.from.flags & osmoh::Time::eSunset)
t1 = sunset;
else
t1 = ptime(d, hours((ts.from.flags & osmoh::Time::eHours) ? ts.from.hours : 0) + minutes((ts.from.flags & osmoh::Time::eMinutes) ? ts.from.minutes : 0));
t2 = t1;
if (ts.to.flags & osmoh::Time::eSunrise)
t2 = sunrise;
else if (ts.to.flags & osmoh::Time::eSunset)
t2 = sunset;
else
{
t2 = ptime(d, hours((ts.to.flags & osmoh::Time::eHours) ? ts.to.hours : 24) + minutes((ts.to.flags & osmoh::Time::eMinutes) ? ts.to.minutes : 0));
if (t2 < t1)
t2 += hours(24);
}
return time_period(t1, t2);
}
} // namespace osmoh
BOOST_FUSION_ADAPT_STRUCT
(
osmoh::Time,
(uint8_t, hours)
(uint8_t, minutes)
(uint8_t, flags)
)
BOOST_FUSION_ADAPT_STRUCT
(
osmoh::TimeSpan,
(osmoh::Time, from)
(osmoh::Time, to)
(uint8_t, flags)
(osmoh::Time, period)
)
BOOST_FUSION_ADAPT_STRUCT
(
osmoh::Weekdays,
(uint8_t, weekdays)
(uint16_t, nth)
(int32_t, offset)
)
BOOST_FUSION_ADAPT_STRUCT
(
osmoh::State,
(uint8_t, state)
(std::string, comment)
)
BOOST_FUSION_ADAPT_STRUCT
(
osmoh::TimeRule,
(std::vector<osmoh::Weekdays>, weekdays)
(std::vector<osmoh::TimeSpan>, timespan)
(osmoh::State, state)
(uint8_t, int_flags)
)
namespace
{
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
namespace charset = boost::spirit::standard_wide;
using space_type = charset::space_type;
class test_impl
{
public:
template <typename T>
struct result { typedef void type; };
template <typename Arg>
void operator() (const Arg & a) const
{
std::cout << a << " \t(" << typeid(a).name() << ")" << std::endl;
}
};
phx::function<test_impl> const test = test_impl();
class dash_ : public qi::symbols<wchar_t>
{
public:
dash_()
{
add
(L"-")
/* not standard */
(L"")(L"")(L"")(L"~")(L"")(L"")(L"to")(L"às")(L"ás")(L"as")(L"a")(L"ate")(L"bis")
;
}
} dash;
class event_ : public qi::symbols<wchar_t, uint8_t>
{
public:
event_()
{
add
(L"dawn", osmoh::Time::eSunrise)(L"sunrise", osmoh::Time::eSunrise)(L"sunset", osmoh::Time::eSunset)(L"dusk", osmoh::Time::eSunset)
;
}
} event;
struct wdays_ : qi::symbols<wchar_t, unsigned>
{
wdays_()
{
add
(L"mo", 0)(L"tu", 1)(L"we", 2)(L"th", 3)(L"fr", 4)(L"sa", 5)(L"su", 6) // 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"", 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)
;
}
} wdays;
struct month_ : qi::symbols<wchar_t, unsigned>
{
month_()
{
add
(L"jan", 1)(L"feb", 2)(L"mar", 3)(L"apr", 4)(L"may", 5)(L"jun", 6)
(L"jul", 7)(L"aug", 8)(L"sep", 9)(L"oct", 10)(L"nov", 11)(L"dec", 12)
;
}
} month;
struct hours_ : qi::symbols<char, uint8_t>
{
hours_()
{
add
( "0", 0)( "1", 1)( "2", 2)( "3", 3)( "4", 4)( "5", 5)( "6", 6)( "7", 7)( "8", 8)( "9", 9) /* not standard */
("00", 0)("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)
;
}
} hours;
struct exthours_ : qi::symbols<char, uint8_t>
{
exthours_()
{
add
( "0", 0)( "1", 1)( "2", 2)( "3", 3)( "4", 4)( "5", 5)( "6", 6)( "7", 7)( "8", 8)( "9", 9) /* not standard */
("00", 0)("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)
;
}
} exthours;
struct minutes_ : qi::symbols<char, uint8_t>
{
minutes_()
{
add
( "0", 0)( "1", 1)( "2", 2)( "3", 3)( "4", 4)( "5", 5)( "6", 6)( "7", 7)( "8", 8)( "9", 9) /* not standard */
("00", 0)("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)("54", 54)("55", 55)("56", 56)("57", 57)("58", 58)("59", 59)
;
}
} minutes;
struct weeknum_ : qi::symbols<char, unsigned>
{
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)
;
}
} weeknum;
struct daynum_ : qi::symbols<char, unsigned>
{
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)
;
}
} daynum;
template <class Iterator>
class year_selector_parser : public qi::grammar<Iterator, space_type>
{
protected:
qi::rule<Iterator, space_type> year;
qi::rule<Iterator, space_type> year_range;
qi::rule<Iterator, space_type> main;
public:
year_selector_parser() : year_selector_parser::base_type(main)
{
using qi::uint_;
using qi::lit;
using charset::char_;
static const qi::int_parser<unsigned, 10, 4, 4> _4digit = {};
year %= _4digit;
year_range %= (year >> dash >> year >> '/' >> uint_)
| (year >> dash >> year)
| year >> char_('+')
| year
;
main %= year_range % ',';
}
};
template <typename Iterator>
class week_selector_parser : public qi::grammar<Iterator, space_type>
{
protected:
qi::rule<Iterator, space_type> week;
qi::rule<Iterator, space_type> year_range;
qi::rule<Iterator, space_type> main;
public:
week_selector_parser() : week_selector_parser::base_type(main)
{
using qi::uint_;
using qi::lit;
using charset::char_;
week %= (weeknum >> dash >> weeknum >> '/' >> uint_)
| (weeknum >> dash >> weeknum)
| weeknum
;
main %= charset::no_case[lit("week")] >> week % ',';
}
};
template <typename Iterator>
class month_selector_parser : public qi::grammar<Iterator, space_type>
{
protected:
qi::rule<Iterator, space_type> date;
qi::rule<Iterator, space_type> day_offset;
qi::rule<Iterator, space_type> date_with_offsets;
qi::rule<Iterator, space_type> monthday_range;
qi::rule<Iterator, space_type> month_range;
qi::rule<Iterator, space_type> main;
public:
month_selector_parser() : month_selector_parser::base_type(main)
{
using qi::int_;
using qi::lit;
using qi::double_;
using qi::lexeme;
using charset::char_;
static const qi::int_parser<unsigned, 10, 4, 4> year = {};
day_offset %= (char_('+') | char_('-')) >> int_ >> charset::no_case[(lit("days") | lit("day"))];
date %= charset::no_case[(-year >> month >> daynum)]
| (-year >> charset::no_case[lit("easter")])
| daynum >> !(lit(':') >> qi::digit)
;
date_with_offsets %= date >> -((char_('+') | char_('-')) >> charset::no_case[wdays] >> qi::no_skip[qi::space]) >> -day_offset;
monthday_range %= (date_with_offsets >> dash >> date_with_offsets)
| (date_with_offsets >> '+')
| date_with_offsets
| charset::no_case[(-year >> month >> dash >> month >> '/' >> int_)]
| charset::no_case[(-year >> month >> dash >> month)]
| charset::no_case[(-year >> month)]
;
month_range %= charset::no_case[(month >> dash >> month)]
| charset::no_case[month]
;
main %= (monthday_range % ',') | (month_range % ',');
BOOST_SPIRIT_DEBUG_NODE(main);
BOOST_SPIRIT_DEBUG_NODE(month_range);
BOOST_SPIRIT_DEBUG_NODE(monthday_range);
BOOST_SPIRIT_DEBUG_NODE(date_with_offsets);
BOOST_SPIRIT_DEBUG_NODE(date);
BOOST_SPIRIT_DEBUG_NODE(day_offset);
}
};
template <typename Iterator>
class weekday_selector_parser : public qi::grammar<Iterator, std::vector<osmoh::Weekdays>(), space_type>
{
protected:
qi::rule<Iterator, uint8_t(), space_type> nth;
qi::rule<Iterator, uint16_t(), space_type> nth_entry;
qi::rule<Iterator, int32_t(), space_type, qi::locals<int8_t>> day_offset;
qi::rule<Iterator, space_type> holyday;
qi::rule<Iterator, space_type> holiday_sequence;
qi::rule<Iterator, osmoh::Weekdays(), space_type> weekday_range;
qi::rule<Iterator, std::vector<osmoh::Weekdays>(), space_type> weekday_sequence;
qi::rule<Iterator, std::vector<osmoh::Weekdays>(), space_type> main;
public:
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 boost::phoenix::at_c;
nth %= ushort_(1) | ushort_(2) | ushort_(3) | ushort_(4) | ushort_(5);
nth_entry = (nth >> dash >> nth) [_val |= ((2 << ((_2-1)-(_1-1))) - 1) << (_1-1)]
| (lit('-') >> nth) [_val |= (0x0100 << (_1 - 1))]
| nth [_val |= (1 << (_1 - 1))]
;
day_offset = (lit('+')[_a = 1] | lit('-') [_a = -1]) >> ushort_[_val = _1*_a] >> charset::no_case[(lit(L"days") | lit(L"day"))];
holyday %= (charset::no_case[lit(L"SH")] >> -day_offset) | charset::no_case[lit(L"PH")];
holiday_sequence %= holyday % ',';
weekday_range = (charset::no_case[wdays][at_c<0>(_val) |= (1<<_1)]
>> L'[' >> nth_entry[at_c<1>(_val) |= _1] % L',' >> L']' >> day_offset[at_c<2>(_val) = _1])
| (charset::no_case[wdays][at_c<0>(_val) |= (1<<_1)] >> L'[' >> nth_entry[at_c<1>(_val) |= _1] % L',' >> L']')
| charset::no_case[(wdays >> dash >> wdays)] [at_c<0>(_val) |= ((2 << ((_2)-(_1))) - 1) << (_1)]
| charset::no_case[wdays][at_c<0>(_val) |= (1<<_1)]
;
weekday_sequence %= (weekday_range % L',') >> !qi::no_skip[charset::alpha] >> -lit(L':');
main = (holiday_sequence >> -lit(L',') >> weekday_sequence[_val = _1])
| weekday_sequence[_val = _1] >> -(-lit(L',') >> holiday_sequence)
| holiday_sequence
;
BOOST_SPIRIT_DEBUG_NODE(main);
BOOST_SPIRIT_DEBUG_NODE(weekday_sequence);
BOOST_SPIRIT_DEBUG_NODE(weekday_range);
BOOST_SPIRIT_DEBUG_NODE(holiday_sequence);
}
};
template <typename Iterator>
class time_selector_parser : public qi::grammar<Iterator, std::vector<osmoh::TimeSpan>(), space_type>
{
protected:
qi::rule<Iterator, osmoh::Time(), space_type, qi::locals<uint8_t>> hour_minutes;
qi::rule<Iterator, osmoh::Time(), space_type, qi::locals<uint8_t>> extended_hour_minutes;
qi::rule<Iterator, osmoh::Time(), 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, std::vector<osmoh::TimeSpan>(), space_type> main;
class validate_timespan_impl
{
public:
template <typename T>
struct result { typedef bool type; };
bool operator() (osmoh::TimeSpan const & ts) const
{
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
using boost::posix_time::hours;
using boost::posix_time::minutes;
using boost::posix_time::time_period;
bool result = true;
if (ts.period.flags)
{
time_period tp = osmoh::make_time_period(boost::gregorian::day_clock::local_day(), ts);
result = (tp.length() >= time_duration(ts.period.hours, ts.period.minutes, 0 /* seconds */));
}
return result;
}
};
public:
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 qi::_pass;
using charset::char_;
using boost::phoenix::at_c;
phx::function<validate_timespan_impl> const validate_timespan = validate_timespan_impl();
hour_minutes = hours[at_c<0>(_val) = _1, at_c<2>(_val) |= osmoh::Time::eHours]
|| (((lit(':') | lit("") | lit('.')) >> minutes[at_c<1>(_val) = _1,
at_c<2>(_val) |= osmoh::Time::eMinutes])
^ charset::no_case[lit('h') | lit("hs") | lit("hrs") | lit("uhr")]
^ (charset::no_case[lit("am")][_a = 0] | charset::no_case[lit("pm")][_a = 1])
[phx::if_(at_c<0>(_val) <= 12)[at_c<0>(_val) += (12 * _a)]])
;
extended_hour_minutes = exthours[at_c<0>(_val) = _1, at_c<2>(_val) |= osmoh::Time::eHours]
|| (((lit(':') | lit("") | lit('.')) >> minutes[at_c<1>(_val) = _1,
at_c<2>(_val) |= osmoh::Time::eMinutes])
^ charset::no_case[lit('h') | lit("hs") | lit("hrs") | lit("uhr")]
^ (charset::no_case[lit("am")][_a = 0] | charset::no_case[lit("pm")][_a = 1])
[phx::if_(at_c<0>(_val) <= 12)[at_c<0>(_val) += (12 * _a)]])
;
variable_time =
(lit('(')
>> charset::no_case[event][at_c<2>(_val) |= _1]
>> (
char_('+')[at_c<2>(_val) |= osmoh::Time::ePlus]
| char_('-')[at_c<2>(_val) |= osmoh::Time::eMinus]
)
>> hour_minutes[at_c<2>(_1) |= at_c<2>(_val), _val = _1]
>> lit(')')
)
| charset::no_case[event][at_c<2>(_val) |= _1]
;
extended_time %= extended_hour_minutes | variable_time;
time %= hour_minutes | variable_time;
timespan =
(time >> dash >> extended_time >> L'/' >> hour_minutes)
[at_c<0>(_val) = _1, at_c<1>(_val) = _2, at_c<2>(_val) |= osmoh::Time::eExt,
at_c<3>(_val) = _3]
| (time >> dash >> extended_time >> L'/' >> minutes)
[at_c<0>(_val) = _1, at_c<1>(_val) = _2, at_c<2>(_val) |= osmoh::Time::eExt,
at_c<1>(at_c<3>(_val)) = _3, at_c<2>(at_c<3>(_val)) = osmoh::Time::eMinutes]
| (time >> dash >> extended_time >> char_(L'+'))
[at_c<0>(_val) = _1, at_c<1>(_val) = _2, at_c<2>(_val) |= osmoh::Time::ePlus]
| (time >> dash >> extended_time)
[at_c<0>(_val) = _1, at_c<1>(_val) = _2]
| (time >> char_(L'+'))
[at_c<0>(_val) = _1, at_c<2>(_val) |= osmoh::Time::ePlus]
| time [at_c<0>(_val) = _1]
;
main %= timespan[_pass = validate_timespan(_1)] % ',';
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);
}
};
template <typename Iterator>
class selectors_parser : public qi::grammar<Iterator, osmoh::TimeRule(), space_type>
{
protected:
weekday_selector_parser<Iterator> weekday_selector;
time_selector_parser<Iterator> time_selector;
year_selector_parser<Iterator> year_selector;
month_selector_parser<Iterator> month_selector;
week_selector_parser<Iterator> week_selector;
qi::rule<Iterator, std::string(), space_type> comment;
qi::rule<Iterator, osmoh::TimeRule(), space_type> small_range_selectors;
qi::rule<Iterator, space_type> wide_range_selectors;
qi::rule<Iterator, osmoh::TimeRule(), space_type> main;
public:
selectors_parser() : selectors_parser::base_type(main)
{
using qi::_1;
using qi::_val;
using qi::lit;
using qi::lexeme;
using charset::char_;
using boost::phoenix::at_c;
using osmoh::State;
comment %= lexeme['"' >> +(char_ - '"') >> '"'];
wide_range_selectors = -year_selector >> -month_selector >> -week_selector >> -lit(':') | (comment >> ':');
small_range_selectors = -weekday_selector[at_c<0>(_val) = _1] >> -( lit("24/7") | time_selector[at_c<1>(_val) = _1]);
main =
(
lit(L"24/7")
| lit(L"24時間営業")
| lit(L"7/24")
| lit(L"24時間")
| charset::no_case[lit(L"daily 24/7")]
| charset::no_case[lit(L"24 hours")]
| charset::no_case[lit(L"24 horas")]
| charset::no_case[lit(L"круглосуточно")]
| charset::no_case[lit(L"24 часа")]
| charset::no_case[lit(L"24 hrs")]
| charset::no_case[lit(L"nonstop")]
| charset::no_case[lit(L"24hrs")]
| charset::no_case[lit(L"open 24 hours")]
| charset::no_case[lit(L"24 stunden")]
)[at_c<0>(at_c<2>(_val)) = State::eOpen]
| (-wide_range_selectors >> small_range_selectors[_val = _1, at_c<0>(at_c<2>(_val)) = State::eOpen])
;
BOOST_SPIRIT_DEBUG_NODE(main);
BOOST_SPIRIT_DEBUG_NODE(small_range_selectors);
BOOST_SPIRIT_DEBUG_NODE(wide_range_selectors);
}
};
template <typename Iterator>
class time_domain_parser : public qi::grammar<Iterator, std::vector<osmoh::TimeRule>(), space_type, qi::locals<qi::rule<Iterator, space_type>*>>
{
protected:
selectors_parser<Iterator> selector_sequence;
qi::rule<Iterator, std::string(), space_type> comment;
qi::rule<Iterator, space_type> separator;
qi::rule<Iterator, space_type> base_separator;
qi::rule<Iterator, osmoh::TimeRule(), space_type> rule_sequence;
qi::rule<Iterator, osmoh::State(), space_type> rule_modifier;
qi::rule<Iterator, std::vector<osmoh::TimeRule>(), space_type, qi::locals<qi::rule<Iterator, space_type>*>> main;
public:
time_domain_parser() : time_domain_parser::base_type(main)
{
using qi::lit;
using qi::lexeme;
using qi::_1;
using qi::_a;
using qi::_val;
using charset::char_;
using boost::phoenix::at_c;
using qi::lazy;
using qi::eps;
using osmoh::State;
comment %= lexeme['"' >> +(char_ - '"') >> '"'] | lexeme['(' >> +(char_ - ')') >> ')'];
base_separator = lit(';') | lit("||");
separator = lit(';') | lit("||") | lit(',');
rule_modifier =
(charset::no_case[lit("open")][at_c<0>(_val) = State::eOpen] >> -comment[at_c<1>(_val) = _1])
| ((charset::no_case[lit("closed") | lit("off")])[at_c<0>(_val) = State::eClosed] >> -comment[at_c<1>(_val) = _1])
| (charset::no_case[lit("unknown")][at_c<0>(_val) = State::eUnknown] >> -comment[at_c<1>(_val) = _1])
| comment[at_c<0>(_val) = State::eUnknown, at_c<1>(_val) = _1]
;
rule_sequence = selector_sequence[_val = _1]
>> -rule_modifier[at_c<2>(_val) = _1, at_c<3>(_val) = 1];
main %= -(lit("opening_hours") >> lit('='))
>> rule_sequence[_a = phx::val(&base_separator),
phx::if_(at_c<3>(_1) || phx::size(at_c<1>(_1)))[_a = phx::val(&separator)]] % lazy(*_a);
BOOST_SPIRIT_DEBUG_NODE(main);
BOOST_SPIRIT_DEBUG_NODE(rule_sequence);
BOOST_SPIRIT_DEBUG_NODE(rule_modifier);
}
};
template <typename Iterator>
bool parse_timerange(Iterator first, Iterator last, std::vector<osmoh::TimeRule> & context)
{
using qi::double_;
using qi::phrase_parse;
using charset::space;
time_domain_parser<Iterator> time_domain;
bool r = phrase_parse(
first, /* start iterator */
last, /* end iterator */
time_domain, /* the parser */
space, /* the skip-parser */
context /* result storage */
);
if (first != last) // fail if we did not get a full match
return false;
return r;
}
bool check_weekday(osmoh::Weekdays const & wd, boost::gregorian::date const & d)
{
using namespace boost::gregorian;
bool hit = false;
typedef nth_day_of_the_week_in_month nth_dow;
if (wd.nth)
{
for (uint8_t i = 0; (wd.weekdays & (0xFF ^ ((1 << i) - 1))); ++i)
{
if (!(wd.weekdays & (1 << i)))
continue;
uint8_t a = wd.nth & 0xFF;
for (size_t j = 0; (a & (0xFF ^ ((1 << j) - 1))); ++j)
{
if (a & (1 << j))
{
nth_dow ndm(nth_dow::week_num(j + 1), nth_dow::day_of_week_type((i + 1 == 7) ? 0 : (i + 1)), d.month());
hit |= (d == ndm.get_date(d.year()));
}
}
a = (wd.nth >> 8) & 0xFF;
for (size_t j = 0; (a & (0xFF ^ ((1 << j) - 1))); ++j)
{
if (a & (1 << j))
{
last_day_of_the_week_in_month lwdm(nth_dow::day_of_week_type((i + 1 == 7) ? 0 : (i + 1)), d.month());
hit |= (d == ((lwdm.get_date(d.year()) - weeks(j)) + days(wd.offset)));
}
}
}
}
else
{
for (uint8_t i = 0; (wd.weekdays & (0xFF ^ ((1 << i) - 1))); ++i)
{
if (!(wd.weekdays & (1 << i)))
continue;
hit |= (d.day_of_week() == ((i + 1 == 7) ? 0 : (i + 1)));
}
}
/* very useful in debug */
// std::cout << d.day_of_week() << " " << d << " --> " << wd << (hit ? " hit" : " miss") << std::endl;
return hit;
}
bool check_timespan(osmoh::TimeSpan const &ts, boost::gregorian::date const & d, boost::posix_time::ptime const & p)
{
using boost::gregorian::days;
using boost::posix_time::ptime;
using boost::posix_time::hours;
using boost::posix_time::minutes;
using boost::posix_time::time_period;
time_period tp1 = osmoh::make_time_period(d-days(1), ts);
time_period tp2 = osmoh::make_time_period(d, ts);
/* very useful in debug */
// std::cout << ts << "\t" << tp1 << "(" << p << ")" << (tp1.contains(p) ? " hit" : " miss") << std::endl;
// std::cout << ts << "\t" << tp2 << "(" << p << ")" << (tp2.contains(p) ? " hit" : " miss") << std::endl;
return tp1.contains(p) || tp2.contains(p);
}
bool check_rule(osmoh::TimeRule const & r, std::tm const & stm, std::ostream * hitcontext = nullptr)
{
bool next = false;
// check 24/7
if (r.weekdays.empty() && r.timespan.empty() && r.state.state == osmoh::State::eOpen)
return true;
boost::gregorian::date date = boost::gregorian::date_from_tm(stm);
boost::posix_time::ptime pt = boost::posix_time::ptime_from_tm(stm);
next = r.weekdays.empty();
for (auto const & wd : r.weekdays)
{
if (check_weekday(wd, date))
{
if (hitcontext)
*hitcontext << wd << " ";
next = true;
}
}
if (!next)
return next;
next = r.timespan.empty();
for (auto const & ts : r.timespan)
{
if (check_timespan(ts, date, pt))
{
if (hitcontext)
*hitcontext << ts << " ";
next = true;
}
}
return next && !(r.timespan.empty() && r.weekdays.empty());
}
} // anonymouse namespace
OSMTimeRange::OSMTimeRange(std::string const & rules)
: m_sourceString(rules)
, m_valid(false)
, m_state(osmoh::State::eUnknown)
{
parse();
}
void OSMTimeRange::parse()
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::wstring src = converter.from_bytes(m_sourceString);
m_valid = parse_timerange(src.begin(), src.end(), m_rules);
}
OSMTimeRange & OSMTimeRange::operator () (time_t timestamp)
{
std::tm stm = *localtime(&timestamp);
osmoh::State::EState true_state[3][3] = {
{osmoh::State::eUnknown, osmoh::State::eClosed, osmoh::State::eOpen},
{osmoh::State::eClosed , osmoh::State::eClosed, osmoh::State::eOpen},
{osmoh::State::eOpen , osmoh::State::eClosed, osmoh::State::eOpen}
};
osmoh::State::EState false_state[3][3] = {
{osmoh::State::eUnknown, osmoh::State::eOpen , osmoh::State::eClosed},
{osmoh::State::eClosed , osmoh::State::eClosed , osmoh::State::eClosed},
{osmoh::State::eOpen , osmoh::State::eOpen , osmoh::State::eOpen}
};
m_state = osmoh::State::eUnknown;
m_comment = std::string();
for (auto const & el : m_rules)
{
bool hit = false;
if ((hit = check_rule(el, stm)))
{
m_state = true_state[m_state][el.state.state];
m_comment = el.state.comment;
}
else
{
m_state = false_state[m_state][el.state.state];
}
/* very useful in debug */
// char const * st[] = {"unknown", "closed", "open"};
// std::cout << "-[" << hit << "]-------------------[" << el << "]: " << st[m_state] << "--------------------" << std::endl;
}
return *this;
}
OSMTimeRange & OSMTimeRange::operator () (std::string const & timestr, char const * timefmt)
{
std::tm when = {};
std::stringstream ss(timestr);
ss >> std::get_time(&when, timefmt);
return this->operator()(std::mktime(&when));
}

View file

@ -1,134 +0,0 @@
/*
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 <string>
#include <vector>
#include <iostream>
namespace osmoh
{
class Time
{
public:
enum EFlags
{
eNone = 0,
eHours = 1,
eMinutes = 2,
ePlus = 4,
eMinus = 8,
eExt = 16,
eSunrise = 32,
eSunset = 64
};
uint8_t hours;
uint8_t minutes;
uint8_t flags;
Time() : hours(0), minutes(0), flags(eNone) {}
inline Time & Hours(uint8_t h) { hours = h; flags |= eHours; return *this; }
inline Time & Minutes(uint8_t m) { minutes = m; flags |= eMinutes; return *this; }
inline Time & Sunset() { flags = eSunset; return *this; }
inline Time & Sunrise() { flags = eSunrise; return *this; }
friend std::ostream & operator << (std::ostream & s, Time const & t);
};
class TimeSpan
{
public:
Time from;
Time to;
uint8_t flags;
Time period;
TimeSpan() : flags(Time::eNone) {}
friend std::ostream & operator << (std::ostream & s, TimeSpan const & span);
};
class Weekdays
{
public:
uint8_t weekdays;
uint16_t nth;
int32_t offset;
Weekdays() : weekdays(0), nth(0), offset(0) {}
friend std::ostream & operator << (std::ostream & s, Weekdays const & w);
};
class State
{
public:
enum EState {
eUnknown = 0,
eClosed = 1,
eOpen = 2
};
uint8_t state;
std::string comment;
State() : state(eUnknown) {}
};
class TimeRule
{
public:
std::vector<Weekdays> weekdays;
std::vector<TimeSpan> timespan;
State state;
uint8_t int_flags = 0;
};
} // namespace osmoh
class OSMTimeRange
{
std::string m_sourceString;
bool m_valid;
osmoh::State::EState m_state;
std::vector<osmoh::TimeRule> m_rules;
std::string m_comment;
public:
OSMTimeRange(std::string const & rules);
inline bool IsValid() const { return m_valid; }
inline bool IsOpen() const { return m_state == osmoh::State::eOpen; }
inline bool IsClosed() const { return m_state == osmoh::State::eClosed; }
inline bool IsUnknown() const { return m_state == osmoh::State::eUnknown; }
inline std::string const & Comment() const { return m_comment; }
OSMTimeRange & operator()(time_t timestamp);
OSMTimeRange & operator()(std::string const & timestr, char const * timefmt="%d-%m-%Y %R");
private:
void parse();
};

View file

@ -0,0 +1,98 @@
#include "parse_opening_hours.hpp"
#include "opening_hours_parsers.hpp"
namespace
{
template <typename Context, typename Iterator>
struct context_parser;
template<typename Iterator> struct context_parser<osmoh::TTimespans, Iterator>
{
using type = osmoh::parsing::time_selector<Iterator>;
};
template<typename Iterator> struct context_parser<osmoh::Weekdays, Iterator>
{
using type = osmoh::parsing::weekday_selector<Iterator>;
};
template<typename Iterator> struct context_parser<osmoh::TMonthdayRanges, Iterator>
{
using type = osmoh::parsing::month_selector<Iterator>;
};
template<typename Iterator> struct context_parser<osmoh::TYearRanges, Iterator>
{
using type = osmoh::parsing::year_selector<Iterator>;
};
template<typename Iterator> struct context_parser<osmoh::TWeekRanges, Iterator>
{
using type = osmoh::parsing::week_selector<Iterator>;
};
template<typename Iterator> struct context_parser<osmoh::TRuleSequences, Iterator>
{
using type = osmoh::parsing::time_domain<Iterator>;
};
template <typename Context, typename Iterator>
using context_parser_t = typename context_parser<Context, Iterator>::type;
template <typename Context>
bool ParseImp(std::string const & str, Context & context)
{
using boost::spirit::qi::phrase_parse;
using boost::spirit::standard_wide::space;
context_parser_t<Context, decltype(begin(str))> 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;
}
} // namespace
namespace osmoh
{
bool Parse(std::string const & str, TTimespans & s)
{
return ParseImp(str, s);
}
bool Parse(std::string const & str, Weekdays & w)
{
return ParseImp(str, w);
}
bool Parse(std::string const & str, TMonthdayRanges & m)
{
return ParseImp(str, m);
}
bool Parse(std::string const & str, TYearRanges & y)
{
return ParseImp(str, y);
}
bool Parse(std::string const & str, TWeekRanges & w)
{
return ParseImp(str, w);
}
bool Parse(std::string const & str, TRuleSequences & r)
{
return ParseImp(str, r);
}
} // namespace osmoh

View file

@ -0,0 +1,14 @@
#pragma once
#include "opening_hours.hpp"
#include <string>
namespace osmoh
{
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

View file

@ -0,0 +1,293 @@
#include "rules_evaluation.hpp"
#include "rules_evaluation_private.hpp"
#include <ctime>
#include <iomanip>
#include <sstream>
#include <tuple>
namespace
{
using THourMinutes = std::tuple<int, int>;
constexpr osmoh::MonthDay::TYear kTMYearOrigin = 1900;
inline bool ToHourMinutes(osmoh::Time const & t, THourMinutes & hm)
{
if (!t.IsHoursMinutes())
return false;
hm = THourMinutes{t.GetHoursCount(), t.GetMinutesCount()};
return true;
}
inline bool ToHourMinutes(std::tm const & t, THourMinutes & hm)
{
hm = THourMinutes{t.tm_hour, t.tm_min};
return true;
}
inline int CompareMonthDayTimeTuple(osmoh::MonthDay const & monthDay, std::tm const & date)
{
if (monthDay.HasYear())
{
if (monthDay.GetYear() != date.tm_year + kTMYearOrigin)
return 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 monthDay.GetDayNum() - date.tm_mday;
}
return 0;
}
inline bool operator<=(osmoh::MonthDay const & monthDay, std::tm const & date)
{
return CompareMonthDayTimeTuple(monthDay, date) < 1;
}
inline bool operator<=(std::tm const & date, osmoh::MonthDay const & monthDay)
{
return CompareMonthDayTimeTuple(monthDay, date) > -1;
}
inline 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.
inline 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;
}
inline 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;
}
inline bool IsBetweenLooped(osmoh::Weekday const start,
osmoh::Weekday const end,
osmoh::Weekday const p)
{
if (start <= end)
return start <= p && p <= end;
return p >= end || start <= p;
}
inline 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;
}
}
} // namespace
namespace osmoh
{
bool IsActive(Timespan const & span, std::tm const & time)
{
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;
// TODO(mgsergio): We don't handle extended hours yet.
// Extended hours handling could be implemented through
// splitting rule with extended hours into separated rules.
// See https://trello.com/c/Efsvs6PP/23-opening-hours-extended-hours
if (end <= start || end > THourMinutes{24, 00})
// It's better to say we are open, cause
// in search result page only `closed' lables are shown.
// So from user's perspective `unknown' and `open'
// mean same as `not closed'.
return true;
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;
if (range.HasEnd())
return IsBetweenLooped(range.GetStart(), range.GetEnd(), wday);
return range.GetStart() == 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())
return
range.GetStart() <= date &&
date <= NormalizeEnd(range.GetStart(), range.GetEnd());
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 IsActive(RuleSequence const & rule, std::tm const & date)
{
if (rule.IsTwentyFourHours())
return true;
return
IsActiveAny(rule.GetYears(), date) &&
IsActiveAny(rule.GetMonths(), date) &&
IsActiveAny(rule.GetWeeks(), date) &&
IsActive(rule.GetWeekdays(), date) &&
IsActiveAny(rule.GetTimes(), date);
}
RuleState GetState(TRuleSequences const & rules, std::tm const & date)
{
auto emptyRuleIt = rules.rend();
for (auto it = rules.rbegin(); it != rules.rend(); ++it)
{
if (IsActive(*it, date))
{
if (it->IsEmpty() && emptyRuleIt == rules.rend())
emptyRuleIt = it;
else
return ModifierToRuleState(it->GetModifier());
}
}
if (emptyRuleIt != rules.rend())
{
if (emptyRuleIt->HasComment())
return RuleState::Unknown;
else
return ModifierToRuleState(emptyRuleIt->GetModifier());
}
return (rules.empty()
? RuleState::Unknown
: RuleState::Closed);
}
RuleState GetState(TRuleSequences const & rules, time_t const dateTime)
{
std::tm tm{};
localtime_r(&dateTime, &tm);
return GetState(rules, tm);
}
} // namespace osmoh

View file

@ -0,0 +1,32 @@
#pragma once
#include "opening_hours.hpp"
#include <ctime>
namespace osmoh
{
enum class RuleState
{
Open,
Closed,
Unknown
};
RuleState GetState(TRuleSequences const & rules, std::tm const & date);
RuleState GetState(TRuleSequences const & rules, time_t const dateTime);
inline bool IsOpen(TRuleSequences const & rules, time_t const dateTime)
{
return GetState(rules, dateTime) == RuleState::Open;
}
inline bool IsClosed(TRuleSequences const & rules, time_t const dateTime)
{
return GetState(rules, dateTime) == RuleState::Closed;
}
inline bool IsUnknown(TRuleSequences const & rules, time_t const dateTime)
{
return GetState(rules, dateTime) == RuleState::Unknown;
}
} // namespace osmoh

View file

@ -0,0 +1,15 @@
#pragma once
#include "opening_hours.hpp"
namespace osmoh
{
bool IsActive(Timespan const & spsn, 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, std::tm const & date);
} // namespace osmoh

View file

@ -5,18 +5,12 @@ TEMPLATE = app
ROOT_DIR = ../..
DEPENDENCIES = indexer platform geometry coding base protobuf tomcrypt
!linux {
DEPENDENCIES += opening_hours
}
include($$ROOT_DIR/common.pri)
QT *= core
!linux {
SOURCES += opening_hours_test.cpp
}
HEADERS += \
test_mwm_set.hpp \
test_polylines.hpp \

View file

@ -1,178 +0,0 @@
#include "../../testing/testing.hpp"
#include "../../3party/opening_hours/osm_time_range.hpp"
UNIT_TEST(OpeningHours_Parse)
{
{
OSMTimeRange oh("06:00-09:00/03");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("06:00-07:00/03");
TEST(oh.IsValid() == false, ());
}
{
OSMTimeRange oh("sunrise-sunset");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("Su-Th sunset-24:00, 04:00-sunrise; Fr-Sa sunset-sunrise");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("Apr-Sep Su [1,3] 14:30-17:00");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("06:00+");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("06:00-07:00+");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("24/7");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("06:13-15:00");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("Mo-Su 08:00-23:00");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("(sunrise+02:00)-(sunset-04:12)");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("Mo-Sa; PH off");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("Jan-Mar 07:00-19:00;Apr-Sep 07:00-22:00;Oct-Dec 07:00-19:00");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("Mo closed");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("06:00-23:00 open \"Dining in\"");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("06:00-23:00 open \"Dining in\" || 00:00-24:00 open \"Drive-through\"");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("Tu-Th 20:00-03:00 open \"Club and bar\"; Fr-Sa 20:00-04:00 open \"Club and bar\" || Su-Mo 18:00-02:00 open \"bar\" || Tu-Th 18:00-03:00 open \"bar\" || Fr-Sa 18:00-04:00 open \"bar\"");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("09:00-21:00 \"call us\"");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("10:00-13:30,17:00-20:30");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("Apr-Sep: Mo-Fr 09:00-13:00,14:00-18:00; Apr-Sep: Sa 10:00-13:00");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("Mo,We,Th,Fr 12:00-18:00; Sa-Su 12:00-17:00");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("Su-Th 11:00-03:00, Fr-Sa 11:00-05:00");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("Mo-We 17:00-01:00, Th,Fr 15:00-01:00; PH off");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
/* test disabled because we go out from DSL definition in some cases */
// OSMTimeRange oh("Tu-Su, Ph 10:00-18:00");
// TEST(oh.IsValid() == false, ("Broken parser"));
}
{
OSMTimeRange oh("Tu-Su 10:00-18:00, Mo 12:00-17:00");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
{
OSMTimeRange oh("06:00-07:00/21:03");
TEST(oh.IsValid() == false, ("Period can't be large then interval"));
}
{
OSMTimeRange oh("sunset-sunrise");
TEST(oh.IsValid(), ("Incorrect schedule string"));
}
}
UNIT_TEST(OpeningHours_TimeHit)
{
{
OSMTimeRange oh("06:13-15:00; 16:30+");
TEST(oh.IsValid(), ("Incorrect schedule string"));
TEST(oh("12-12-2013 7:00").IsOpen(), ());
TEST(oh("12-12-2013 16:00").IsClosed(), ());
TEST(oh("12-12-2013 20:00").IsOpen(), ());
}
{
OSMTimeRange oh("We-Sa; Mo[1,3] closed; Su[-1,-2] closed; Fr[2] open; Fr[-2], Fr open; Su[-2] -2 days");
TEST(oh.IsValid(), ("Incorrect schedule string"));
TEST(oh("20-03-2015 18:00").IsOpen(), ());
TEST(oh("17-03-2015 18:00").IsClosed(), ());
}
{
OSMTimeRange oh("We-Fr; Mo[1,3] closed; Su[-1,-2] closed");
TEST(oh.IsValid(), ("Incorrect schedule string"));
TEST(oh("20-03-2015 18:00").IsOpen(), ());
TEST(oh("17-03-2015 18:00").IsClosed(), ());
}
{
OSMTimeRange oh("We-Fr; Mo[1,3] +1 day closed; Su[-1,-2] -3 days closed");
TEST(oh.IsValid(), ("Incorrect schedule string"));
TEST(oh("20-03-2015 18:00").IsOpen(), ());
TEST(oh("17-03-2015 18:00").IsClosed(), ());
}
{
OSMTimeRange oh("Mo-Su 14:30-17:00; Mo[1] closed; Su[-1] closed");
TEST(oh.IsValid(), ("Incorrect schedule string"));
TEST(oh("09-03-2015 16:00").IsOpen(), ());
TEST(oh("02-03-2015 16:00").IsClosed(), ());
TEST(oh("22-03-2015 16:00").IsOpen(), ());
TEST(oh("29-03-2015 16:00").IsClosed(), ());
}
{
OSMTimeRange oh("PH,Tu-Su 10:00-18:00; Sa[1] 10:00-18:00 open \"Eintritt ins gesamte Haus frei\"; Jan 1,Dec 24,Dec 25,easter -2 days: closed");
TEST(oh.IsValid(), ("Incorrect schedule string"));
TEST(oh("03-03-2015 16:00").IsOpen(), ());
TEST(oh.Comment().empty(), ());
TEST(oh("07-03-2015 16:00").IsOpen(), ());
TEST(oh.Comment().empty() == false, ());
}
{
OSMTimeRange oh("Mo-Su 11:00+; Mo [1,3] off");
TEST(oh.IsValid(), ("Incorrect schedule string"));
TEST(oh("04-03-2015 16:00").IsOpen(), ());
TEST(oh("09-03-2015 16:00").IsOpen(), ());
TEST(oh("02-03-2015 16:00").IsClosed(), ());
TEST(oh("16-03-2015 16:00").IsClosed(), ());
}
{
OSMTimeRange oh("08:00-16:00 open, 16:00-03:00 open \"public room\"");
TEST(oh.IsValid(), ("Incorrect schedule string"));
TEST(oh("01-03-2015 20:00").IsOpen(), ());
TEST(oh("01-03-2015 20:00").Comment() == "public room", ());
}
}

View file

@ -9,9 +9,7 @@ ROOT_DIR = ../..
DEPENDENCIES = map render gui routing search storage graphics indexer platform anim geometry coding base \
freetype fribidi expat protobuf tomcrypt jansson osrm stats_client minizip succinct
!linux* {
DEPENDENCIES *= opening_hours
}
DEPENDENCIES *= opening_hours
drape {
DEPENDENCIES *= drape_frontend drape

View file

@ -1,6 +1,6 @@
#pragma once
#include "3party/opening_hours/osm_time_range.hpp"
#include "3party/opening_hours/opening_hours.hpp"
#include "std/chrono.hpp"
@ -31,11 +31,23 @@ inline string DebugPrint(EPlaceState state)
inline EPlaceState PlaceStateCheck(string const & openingHours, time_t timestamp)
{
OSMTimeRange oh(openingHours);
osmoh::OpeningHours oh(openingHours);
auto future = system_clock::from_time_t(timestamp);
future += minutes(15);
size_t nowState = oh(timestamp).IsOpen() ? 0 : 1;
size_t futureState = oh(system_clock::to_time_t(future)).IsOpen() ? 0 : 1;
enum {OPEN = 0, CLOSED = 1};
size_t nowState = OPEN;
size_t futureState = OPEN;
// TODO(mgsergio): Switch to three-stated model instead of two-staed
// I.e. set unknown if we can't parse or can't answer whether it's open.
if (oh.IsValid())
{
nowState = oh.IsOpen(timestamp) ? OPEN : CLOSED;
futureState = oh.IsOpen(system_clock::to_time_t(future)) ? OPEN : CLOSED;
}
EPlaceState state[2][2] = {{EPlaceState::Open, EPlaceState::CloseSoon},
{EPlaceState::OpenSoon, EPlaceState::Closed}};

View file

@ -4,10 +4,7 @@ DEPENDENCIES = map render gui routing search storage indexer graphics platform a
freetype expat fribidi tomcrypt jansson protobuf osrm stats_client minizip succinct
!linux* {
DEPENDENCIES += opening_hours \
}
DEPENDENCIES += opening_hours \
drape {
DEPENDENCIES *= drape_frontend drape

View file

@ -1,4 +1,4 @@
# This subproject implements integration tests.
# This subproject implements integration tests.
# This tests are launched on the whole world dataset.
# It is recommended to place tests here in the following cases:
@ -13,10 +13,7 @@ TEMPLATE = app
ROOT_DIR = ../..
DEPENDENCIES = map routing search storage indexer platform geometry coding base osrm jansson protobuf tomcrypt succinct stats_client
# this dependency is not built on Linux
!linux* {
DEPENDENCIES += opening_hours
}
DEPENDENCIES += opening_hours
macx-*: LIBS *= "-framework IOKit" "-framework SystemConfiguration"

View file

@ -16,10 +16,7 @@
#include "base/string_utils.hpp"
#include "base/logging.hpp"
#ifndef OMIM_OS_LINUX
// Lib opening_hours is not built for Linux since stdlib doesn't have required functions.
#include "3party/opening_hours/osm_time_range.hpp"
#endif
#include "3party/opening_hours/opening_hours.hpp"
namespace search
@ -37,12 +34,18 @@ void ProcessMetadata(FeatureType const & ft, Result::Metadata & meta)
meta.m_cuisine = src.Get(feature::Metadata::FMD_CUISINE);
#ifndef OMIM_OS_LINUX
// Lib opening_hours is not built for Linux since stdlib doesn't have required functions.
string const openHours = src.Get(feature::Metadata::FMD_OPEN_HOURS);
if (!openHours.empty())
meta.m_isClosed = OSMTimeRange(openHours)(time(nullptr)).IsClosed();
#endif
{
osmoh::OpeningHours oh(openHours);
// TODO(mgsergio): Switch to three-stated model instead of two-staed
// I.e. set unknown if we can't parse or can't answer whether it's open.
if (oh.IsValid())
meta.m_isClosed = oh.IsClosed(time(nullptr));
else
meta.m_isClosed = false;
}
meta.m_stars = 0;
(void) strings::to_int(src.Get(feature::Metadata::FMD_STARS), meta.m_stars);

View file

@ -9,10 +9,8 @@ ROOT_DIR = ../..
DEPENDENCIES = generator routing search storage stats_client jansson indexer platform geometry coding base \
tess2 protobuf tomcrypt
!linux* {
DEPENDENCIES += opening_hours \
DEPENDENCIES += opening_hours \
}
include($$ROOT_DIR/common.pri)

View file

@ -7,10 +7,27 @@
objects = {
/* Begin PBXBuildFile section */
670C61FA1AC3517200C38A8C /* osm_time_range.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 670C61F71AC3517200C38A8C /* osm_time_range.hpp */; };
671C61FF1AE7AF4000076BD0 /* osm_time_range.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 670C61F61AC3517200C38A8C /* osm_time_range.cpp */; };
671C62011AE7AF4400076BD0 /* osm_time_range.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 670C61F61AC3517200C38A8C /* osm_time_range.cpp */; };
671C62021AE7AF4800076BD0 /* osm_time_range_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 670C61F51AC3517200C38A8C /* osm_time_range_tests.cpp */; };
E91738901BECCB5400717F6E /* opening_hours_parsers_terminals.hpp in Headers */ = {isa = PBXBuildFile; fileRef = E91738861BECCB5400717F6E /* opening_hours_parsers_terminals.hpp */; };
E91738911BECCB5400717F6E /* opening_hours_parsers.hpp in Headers */ = {isa = PBXBuildFile; fileRef = E91738871BECCB5400717F6E /* opening_hours_parsers.hpp */; };
E91738921BECCB5400717F6E /* opening_hours.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E91738881BECCB5400717F6E /* opening_hours.cpp */; };
E91738931BECCB5400717F6E /* opening_hours.hpp in Headers */ = {isa = PBXBuildFile; fileRef = E91738891BECCB5400717F6E /* opening_hours.hpp */; };
E91738941BECCB5400717F6E /* parse_opening_hours.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E917388B1BECCB5400717F6E /* parse_opening_hours.cpp */; };
E91738951BECCB5400717F6E /* parse_opening_hours.hpp in Headers */ = {isa = PBXBuildFile; fileRef = E917388C1BECCB5400717F6E /* parse_opening_hours.hpp */; };
E91738961BECCB5400717F6E /* rules_evaluation_private.hpp in Headers */ = {isa = PBXBuildFile; fileRef = E917388D1BECCB5400717F6E /* rules_evaluation_private.hpp */; };
E91738971BECCB5400717F6E /* rules_evaluation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E917388E1BECCB5400717F6E /* rules_evaluation.cpp */; };
E91738981BECCB5400717F6E /* rules_evaluation.hpp in Headers */ = {isa = PBXBuildFile; fileRef = E917388F1BECCB5400717F6E /* rules_evaluation.hpp */; };
E917389C1BECCC1000717F6E /* libopening_hours.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 670C61E71AC3511700C38A8C /* libopening_hours.a */; };
E91738AE1BECCFAC00717F6E /* opening_hours_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E91738A81BECCF4800717F6E /* opening_hours_tests.cpp */; };
E91738BA1BECD03B00717F6E /* opening_hours_supported_features_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E91738AC1BECCF9C00717F6E /* opening_hours_supported_features_tests.cpp */; };
E91738BB1BECD05A00717F6E /* libopening_hours.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 670C61E71AC3511700C38A8C /* libopening_hours.a */; };
E91738C71BECD09100717F6E /* libopening_hours.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 670C61E71AC3511700C38A8C /* libopening_hours.a */; };
E91738C81BECD09700717F6E /* opening_hours_integration_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E91738AA1BECCF7A00717F6E /* opening_hours_integration_tests.cpp */; };
E91738CA1BECD0E100717F6E /* opening-count.lst in CopyFiles */ = {isa = PBXBuildFile; fileRef = E91738C91BECD0CE00717F6E /* opening-count.lst */; };
E91739021BECD54300717F6E /* opening_hours_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E91738A81BECCF4800717F6E /* opening_hours_tests.cpp */; };
E91739031BECD55700717F6E /* libopening_hours.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 670C61E71AC3511700C38A8C /* libopening_hours.a */; };
E917391F1BECD61400717F6E /* opening_hours_integration_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E91738AA1BECCF7A00717F6E /* opening_hours_integration_tests.cpp */; };
E91739201BECD61A00717F6E /* libopening_hours.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 670C61E71AC3511700C38A8C /* libopening_hours.a */; };
E91739211BECD62E00717F6E /* opening-count.lst in Resources */ = {isa = PBXBuildFile; fileRef = E91738C91BECD0CE00717F6E /* opening-count.lst */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -23,15 +40,49 @@
);
runOnlyForDeploymentPostprocessing = 1;
};
E91738B11BECD02B00717F6E /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
E91738BE1BECD08500717F6E /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 12;
dstPath = "";
dstSubfolderSpec = 6;
files = (
E91738CA1BECD0E100717F6E /* opening-count.lst in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
670C61E71AC3511700C38A8C /* libopening_hours.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libopening_hours.a; sourceTree = BUILT_PRODUCTS_DIR; };
670C61F51AC3517200C38A8C /* osm_time_range_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = osm_time_range_tests.cpp; sourceTree = "<group>"; };
670C61F61AC3517200C38A8C /* osm_time_range.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = osm_time_range.cpp; sourceTree = "<group>"; };
670C61F71AC3517200C38A8C /* osm_time_range.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = osm_time_range.hpp; sourceTree = "<group>"; };
670C61FF1AC351AC00C38A8C /* opening_hours_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = opening_hours_test; sourceTree = BUILT_PRODUCTS_DIR; };
670D05B71B0E0C290013A7AC /* defaults.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = defaults.xcconfig; path = ../defaults.xcconfig; sourceTree = "<group>"; };
E91738861BECCB5400717F6E /* opening_hours_parsers_terminals.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = opening_hours_parsers_terminals.hpp; sourceTree = "<group>"; };
E91738871BECCB5400717F6E /* opening_hours_parsers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = opening_hours_parsers.hpp; sourceTree = "<group>"; };
E91738881BECCB5400717F6E /* opening_hours.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opening_hours.cpp; sourceTree = "<group>"; };
E91738891BECCB5400717F6E /* opening_hours.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = opening_hours.hpp; sourceTree = "<group>"; };
E917388B1BECCB5400717F6E /* parse_opening_hours.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parse_opening_hours.cpp; sourceTree = "<group>"; };
E917388C1BECCB5400717F6E /* parse_opening_hours.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = parse_opening_hours.hpp; sourceTree = "<group>"; };
E917388D1BECCB5400717F6E /* rules_evaluation_private.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rules_evaluation_private.hpp; sourceTree = "<group>"; };
E917388E1BECCB5400717F6E /* rules_evaluation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rules_evaluation.cpp; sourceTree = "<group>"; };
E917388F1BECCB5400717F6E /* rules_evaluation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rules_evaluation.hpp; sourceTree = "<group>"; };
E91738A81BECCF4800717F6E /* opening_hours_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opening_hours_tests.cpp; sourceTree = "<group>"; };
E91738AA1BECCF7A00717F6E /* opening_hours_integration_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opening_hours_integration_tests.cpp; sourceTree = "<group>"; };
E91738AC1BECCF9C00717F6E /* opening_hours_supported_features_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opening_hours_supported_features_tests.cpp; sourceTree = "<group>"; };
E91738B31BECD02B00717F6E /* opening_hours_supported_features_tests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = opening_hours_supported_features_tests; sourceTree = BUILT_PRODUCTS_DIR; };
E91738C01BECD08500717F6E /* opening_hours_integration_tests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = opening_hours_integration_tests; sourceTree = BUILT_PRODUCTS_DIR; };
E91738C91BECD0CE00717F6E /* opening-count.lst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "opening-count.lst"; sourceTree = "<group>"; };
E91738CF1BECD36E00717F6E /* opening_hours_tests_ios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = opening_hours_tests_ios.app; sourceTree = BUILT_PRODUCTS_DIR; };
E91738E21BECD36E00717F6E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E91739081BECD60000717F6E /* opening_hours_integration_tests_ios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = opening_hours_integration_tests_ios.app; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -46,6 +97,39 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E917389C1BECCC1000717F6E /* libopening_hours.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E91738B01BECD02B00717F6E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E91738BB1BECD05A00717F6E /* libopening_hours.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E91738BD1BECD08500717F6E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E91738C71BECD09100717F6E /* libopening_hours.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E91738CC1BECD36E00717F6E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E91739031BECD55700717F6E /* libopening_hours.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E91739051BECD60000717F6E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E91739201BECD61A00717F6E /* libopening_hours.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -56,7 +140,11 @@
isa = PBXGroup;
children = (
670D05B71B0E0C290013A7AC /* defaults.xcconfig */,
E91738A71BECCEDB00717F6E /* opening_hours_supported_features_tests */,
E91738A61BECCED700717F6E /* opening_hours_integration_tests */,
E91738A51BECCEB000717F6E /* opening_hours_tests */,
670C61E91AC3511700C38A8C /* opening_hours */,
E91738D01BECD36E00717F6E /* opening_hours_tests_ios */,
670C61E81AC3511700C38A8C /* Products */,
);
sourceTree = "<group>";
@ -66,6 +154,10 @@
children = (
670C61E71AC3511700C38A8C /* libopening_hours.a */,
670C61FF1AC351AC00C38A8C /* opening_hours_test */,
E91738B31BECD02B00717F6E /* opening_hours_supported_features_tests */,
E91738C01BECD08500717F6E /* opening_hours_integration_tests */,
E91738CF1BECD36E00717F6E /* opening_hours_tests_ios.app */,
E91739081BECD60000717F6E /* opening_hours_integration_tests_ios.app */,
);
name = Products;
sourceTree = "<group>";
@ -73,14 +165,56 @@
670C61E91AC3511700C38A8C /* opening_hours */ = {
isa = PBXGroup;
children = (
670C61F51AC3517200C38A8C /* osm_time_range_tests.cpp */,
670C61F61AC3517200C38A8C /* osm_time_range.cpp */,
670C61F71AC3517200C38A8C /* osm_time_range.hpp */,
E91738861BECCB5400717F6E /* opening_hours_parsers_terminals.hpp */,
E91738871BECCB5400717F6E /* opening_hours_parsers.hpp */,
E91738881BECCB5400717F6E /* opening_hours.cpp */,
E91738891BECCB5400717F6E /* opening_hours.hpp */,
E917388B1BECCB5400717F6E /* parse_opening_hours.cpp */,
E917388C1BECCB5400717F6E /* parse_opening_hours.hpp */,
E917388D1BECCB5400717F6E /* rules_evaluation_private.hpp */,
E917388E1BECCB5400717F6E /* rules_evaluation.cpp */,
E917388F1BECCB5400717F6E /* rules_evaluation.hpp */,
);
name = opening_hours;
path = ../../3party/opening_hours;
sourceTree = "<group>";
};
E91738A51BECCEB000717F6E /* opening_hours_tests */ = {
isa = PBXGroup;
children = (
E91738A81BECCF4800717F6E /* opening_hours_tests.cpp */,
);
name = opening_hours_tests;
path = ../../3party/opening_hours/opening_hours_tests;
sourceTree = "<group>";
};
E91738A61BECCED700717F6E /* opening_hours_integration_tests */ = {
isa = PBXGroup;
children = (
E91738C91BECD0CE00717F6E /* opening-count.lst */,
E91738AA1BECCF7A00717F6E /* opening_hours_integration_tests.cpp */,
);
name = opening_hours_integration_tests;
path = ../../3party/opening_hours/opening_hours_integration_tests;
sourceTree = "<group>";
};
E91738A71BECCEDB00717F6E /* opening_hours_supported_features_tests */ = {
isa = PBXGroup;
children = (
E91738AC1BECCF9C00717F6E /* opening_hours_supported_features_tests.cpp */,
);
name = opening_hours_supported_features_tests;
path = ../../3party/opening_hours/opening_hours_supported_features_tests;
sourceTree = "<group>";
};
E91738D01BECD36E00717F6E /* opening_hours_tests_ios */ = {
isa = PBXGroup;
children = (
E91738E21BECD36E00717F6E /* Info.plist */,
);
path = opening_hours_tests_ios;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -88,7 +222,12 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
670C61FA1AC3517200C38A8C /* osm_time_range.hpp in Headers */,
E91738981BECCB5400717F6E /* rules_evaluation.hpp in Headers */,
E91738911BECCB5400717F6E /* opening_hours_parsers.hpp in Headers */,
E91738931BECCB5400717F6E /* opening_hours.hpp in Headers */,
E91738901BECCB5400717F6E /* opening_hours_parsers_terminals.hpp in Headers */,
E91738951BECCB5400717F6E /* parse_opening_hours.hpp in Headers */,
E91738961BECCB5400717F6E /* rules_evaluation_private.hpp in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -129,6 +268,74 @@
productReference = 670C61FF1AC351AC00C38A8C /* opening_hours_test */;
productType = "com.apple.product-type.tool";
};
E91738B21BECD02B00717F6E /* opening_hours_supported_features_tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = E91738B71BECD02B00717F6E /* Build configuration list for PBXNativeTarget "opening_hours_supported_features_tests" */;
buildPhases = (
E91738AF1BECD02B00717F6E /* Sources */,
E91738B01BECD02B00717F6E /* Frameworks */,
E91738B11BECD02B00717F6E /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = opening_hours_supported_features_tests;
productName = opening_hours_supported_features_tests;
productReference = E91738B31BECD02B00717F6E /* opening_hours_supported_features_tests */;
productType = "com.apple.product-type.tool";
};
E91738BF1BECD08500717F6E /* opening_hours_integration_tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = E91738C41BECD08500717F6E /* Build configuration list for PBXNativeTarget "opening_hours_integration_tests" */;
buildPhases = (
E91738BC1BECD08500717F6E /* Sources */,
E91738BD1BECD08500717F6E /* Frameworks */,
E91738BE1BECD08500717F6E /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = opening_hours_integration_tests;
productName = opening_hours_integration_tests;
productReference = E91738C01BECD08500717F6E /* opening_hours_integration_tests */;
productType = "com.apple.product-type.tool";
};
E91738CE1BECD36E00717F6E /* opening_hours_tests_ios */ = {
isa = PBXNativeTarget;
buildConfigurationList = E91738F91BECD36F00717F6E /* Build configuration list for PBXNativeTarget "opening_hours_tests_ios" */;
buildPhases = (
E91738CB1BECD36E00717F6E /* Sources */,
E91738CC1BECD36E00717F6E /* Frameworks */,
E91738CD1BECD36E00717F6E /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = opening_hours_tests_ios;
productName = opening_hours_tests_ios;
productReference = E91738CF1BECD36E00717F6E /* opening_hours_tests_ios.app */;
productType = "com.apple.product-type.application";
};
E91739071BECD60000717F6E /* opening_hours_integration_tests_ios */ = {
isa = PBXNativeTarget;
buildConfigurationList = E917391C1BECD60000717F6E /* Build configuration list for PBXNativeTarget "opening_hours_integration_tests_ios" */;
buildPhases = (
E91739041BECD60000717F6E /* Sources */,
E91739051BECD60000717F6E /* Frameworks */,
E91739061BECD60000717F6E /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = opening_hours_integration_tests_ios;
productName = opening_hours_integration_tests_ios;
productReference = E91739081BECD60000717F6E /* opening_hours_integration_tests_ios.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@ -144,6 +351,18 @@
670C61FE1AC351AC00C38A8C = {
CreatedOnToolsVersion = 6.1.1;
};
E91738B21BECD02B00717F6E = {
CreatedOnToolsVersion = 7.1;
};
E91738BF1BECD08500717F6E = {
CreatedOnToolsVersion = 7.1;
};
E91738CE1BECD36E00717F6E = {
CreatedOnToolsVersion = 7.1;
};
E91739071BECD60000717F6E = {
CreatedOnToolsVersion = 7.1;
};
};
};
buildConfigurationList = 670C61E21AC3511700C38A8C /* Build configuration list for PBXProject "opening_hours" */;
@ -152,6 +371,7 @@
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 670C61DE1AC3511700C38A8C;
productRefGroup = 670C61E81AC3511700C38A8C /* Products */;
@ -160,16 +380,40 @@
targets = (
670C61E61AC3511700C38A8C /* opening_hours */,
670C61FE1AC351AC00C38A8C /* opening_hours_test */,
E91738B21BECD02B00717F6E /* opening_hours_supported_features_tests */,
E91738BF1BECD08500717F6E /* opening_hours_integration_tests */,
E91738CE1BECD36E00717F6E /* opening_hours_tests_ios */,
E91739071BECD60000717F6E /* opening_hours_integration_tests_ios */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
E91738CD1BECD36E00717F6E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
E91739061BECD60000717F6E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E91739211BECD62E00717F6E /* opening-count.lst in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
670C61E31AC3511700C38A8C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
671C61FF1AE7AF4000076BD0 /* osm_time_range.cpp in Sources */,
E91738941BECCB5400717F6E /* parse_opening_hours.cpp in Sources */,
E91738921BECCB5400717F6E /* opening_hours.cpp in Sources */,
E91738971BECCB5400717F6E /* rules_evaluation.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -177,8 +421,39 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
671C62021AE7AF4800076BD0 /* osm_time_range_tests.cpp in Sources */,
671C62011AE7AF4400076BD0 /* osm_time_range.cpp in Sources */,
E91738AE1BECCFAC00717F6E /* opening_hours_tests.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E91738AF1BECD02B00717F6E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E91738BA1BECD03B00717F6E /* opening_hours_supported_features_tests.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E91738BC1BECD08500717F6E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E91738C81BECD09700717F6E /* opening_hours_integration_tests.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E91738CB1BECD36E00717F6E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E91739021BECD54300717F6E /* opening_hours_tests.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E91739041BECD60000717F6E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E917391F1BECD61400717F6E /* opening_hours_integration_tests.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -313,6 +588,124 @@
};
name = Release;
};
E91738B81BECD02B00717F6E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "-";
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
GCC_NO_COMMON_BLOCKS = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
E91738B91BECD02B00717F6E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
GCC_NO_COMMON_BLOCKS = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
E91738C51BECD08500717F6E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "-";
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
GCC_NO_COMMON_BLOCKS = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
E91738C61BECD08500717F6E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
GCC_NO_COMMON_BLOCKS = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
E91738FA1BECD36F00717F6E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = opening_hours_tests_ios/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "mail.ru.opening-hours-tests-ios";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
E91738FB1BECD36F00717F6E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = opening_hours_tests_ios/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "mail.ru.opening-hours-tests-ios";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
E917391D1BECD60000717F6E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = opening_hours_integration_tests_ios/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "mail.ru.opening-hours-integration-tests-ios";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
E917391E1BECD60000717F6E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = opening_hours_integration_tests_ios/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "mail.ru.opening-hours-integration-tests-ios";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -343,6 +736,42 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
E91738B71BECD02B00717F6E /* Build configuration list for PBXNativeTarget "opening_hours_supported_features_tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E91738B81BECD02B00717F6E /* Debug */,
E91738B91BECD02B00717F6E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
E91738C41BECD08500717F6E /* Build configuration list for PBXNativeTarget "opening_hours_integration_tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E91738C51BECD08500717F6E /* Debug */,
E91738C61BECD08500717F6E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
E91738F91BECD36F00717F6E /* Build configuration list for PBXNativeTarget "opening_hours_tests_ios" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E91738FA1BECD36F00717F6E /* Debug */,
E91738FB1BECD36F00717F6E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
E917391C1BECD60000717F6E /* Build configuration list for PBXNativeTarget "opening_hours_integration_tests_ios" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E917391D1BECD60000717F6E /* Debug */,
E917391E1BECD60000717F6E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 670C61DF1AC3511700C38A8C /* Project object */;

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>