diff --git a/include/hueplusplus/TimePattern.h b/include/hueplusplus/TimePattern.h
new file mode 100644
index 0000000..d1183a5
--- /dev/null
+++ b/include/hueplusplus/TimePattern.h
@@ -0,0 +1,198 @@
+/**
+ \file TimePattern.h
+ Copyright Notice\n
+ Copyright (C) 2020 Jan Rogall - developer\n
+
+ This file is part of hueplusplus.
+
+ hueplusplus is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ hueplusplus is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with hueplusplus. If not, see .
+**/
+
+#ifndef INCLUDE_HUEPLUSPLUS_TIME_PATTERN
+#define INCLUDE_HUEPLUSPLUS_TIME_PATTERN
+
+#include
+#include
+
+namespace hueplusplus
+{
+namespace time
+{
+std::string timepointToTimestamp(std::chrono::system_clock::time_point time);
+std::chrono::system_clock::time_point parseTimestamp(const std::string& timestamp);
+
+std::string durationTo_hh_mm_ss(std::chrono::system_clock::duration duration);
+std::chrono::system_clock::duration parseDuration(const std::string& hourMinSec);
+
+class AbsoluteTime
+{
+ using clock = std::chrono::system_clock;
+
+public:
+ explicit AbsoluteTime(clock::time_point baseTime, clock::duration variation = std::chrono::seconds(0));
+
+ clock::time_point getBaseTime();
+ clock::duration getRandomVariation();
+
+ std::string toString();
+
+private:
+ clock::time_point base;
+ clock::duration variation;
+};
+class Weekdays
+{
+public:
+ Weekdays() : bitmask(0) {}
+ explicit Weekdays(int num) : bitmask(1 << num) {}
+
+ bool isNone() const;
+ bool isAll() const;
+ bool isMonday() const;
+ bool isTuesday() const;
+ bool isWednesday() const;
+ bool isThursday() const;
+ bool isFriday() const;
+ bool isSaturday() const;
+ bool isSunday() const;
+
+ Weekdays unionWith(Weekdays other) const;
+ Weekdays operator|(Weekdays other) const { return unionWith(other); }
+
+ std::string toString() const;
+
+ static Weekdays none();
+ static Weekdays all();
+ static Weekdays monday();
+ static Weekdays tuesday();
+ static Weekdays wednesday();
+ static Weekdays thursday();
+ static Weekdays friday();
+ static Weekdays saturday();
+ static Weekdays sunday();
+
+ static Weekdays parse(const std::string& s);
+private:
+ int bitmask;
+};
+class RecurringTime
+{
+ using clock = std::chrono::system_clock;
+
+public:
+ explicit RecurringTime(clock::duration daytime, Weekdays days, clock::duration variation = std::chrono::seconds(0));
+
+ clock::duration getDaytime() const;
+ clock::duration getRandomVariation() const;
+ Weekdays getWeekdays() const;
+
+ std::string toString() const;
+
+private:
+ clock::duration time;
+ clock::duration variation;
+ Weekdays days;
+};
+class TimeInterval
+{
+ using clock = std::chrono::system_clock;
+
+public:
+ TimeInterval(clock::duration start, clock::duration end, Weekdays days = Weekdays::all());
+
+ clock::duration getStartTime() const;
+ clock::duration getEndTime() const;
+ Weekdays getWeekdays() const;
+
+ std::string toString() const;
+
+private:
+ clock::duration start;
+ clock::duration end;
+ Weekdays days;
+};
+class Timer
+{
+ using clock = std::chrono::system_clock;
+
+public:
+ Timer(clock::duration duration, clock::duration variation = std::chrono::seconds(0));
+ Timer(clock::duration duration, int numExecutions, clock::duration variation = std::chrono::seconds(0));
+
+ bool isRecurring() const;
+ int getNumberOfExecutions() const;
+ clock::duration getExpiryTime() const;
+ clock::duration getRandomVariation() const;
+
+ std::string toString() const;
+
+private:
+ clock::duration expires;
+ clock::duration variation;
+ int numExecutions;
+};
+
+class TimePattern
+{
+public:
+ enum class Type
+ {
+ undefined,
+ absolute,
+ recurring,
+ interval,
+ timer
+ };
+
+ TimePattern();
+ ~TimePattern();
+ explicit TimePattern(const AbsoluteTime& absolute);
+ explicit TimePattern(const RecurringTime& recurring);
+ explicit TimePattern(const TimeInterval& interval);
+ explicit TimePattern(const Timer& timer);
+
+ TimePattern(const TimePattern& other);
+
+ TimePattern& operator=(const TimePattern& other);
+
+ Type getType() const;
+
+ AbsoluteTime asAbsolute() const;
+
+ RecurringTime asRecurring() const;
+
+ TimeInterval asInterval() const;
+
+ Timer asTimer() const;
+
+ static TimePattern parse(const std::string& s);
+
+private:
+ void destroy();
+
+private:
+ Type type;
+ union
+ {
+ nullptr_t undefined;
+ AbsoluteTime absolute;
+ RecurringTime recurring;
+ TimeInterval interval;
+ Timer timer;
+ };
+};
+} // namespace time
+} // namespace hueplusplus
+
+#endif
\ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 74238fc..486f6ba 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -12,9 +12,10 @@ set(hueplusplus_SOURCES
SimpleBrightnessStrategy.cpp
SimpleColorHueStrategy.cpp
SimpleColorTemperatureStrategy.cpp
+ StateTransaction.cpp
+ "TimePattern.cpp"
UPnP.cpp
- Utils.cpp
- StateTransaction.cpp)
+ Utils.cpp)
# on windows we want to compile the WinHttpHandler
if(WIN32)
diff --git a/src/TimePattern.cpp b/src/TimePattern.cpp
new file mode 100644
index 0000000..b8c5cba
--- /dev/null
+++ b/src/TimePattern.cpp
@@ -0,0 +1,545 @@
+/**
+ \file TimePattern.cpp
+ Copyright Notice\n
+ Copyright (C) 2020 Jan Rogall - developer\n
+
+ This file is part of hueplusplus.
+
+ hueplusplus is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ hueplusplus is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with hueplusplus. If not, see .
+**/
+
+#include
+
+#include
+#include
+
+namespace hueplusplus
+{
+namespace time
+{
+
+using clock = std::chrono::system_clock;
+std::string timepointToTimestamp(clock::time_point time)
+{
+ using namespace std::chrono;
+ std::time_t ctime = clock::to_time_t(time);
+
+ std::tm localtime = *std::localtime(&ctime);
+ char buf[32];
+
+ std::size_t result = std::strftime(buf, sizeof(buf), "%FT%T", &localtime);
+ if (result == 0)
+ {
+ throw HueException(CURRENT_FILE_INFO, "strftime failed");
+ }
+ return std::string(buf);
+}
+
+clock::time_point parseTimestamp(const std::string& timestamp)
+{
+ std::tm tm;
+ tm.tm_year = std::stoi(timestamp.substr(0, 4));
+ tm.tm_mon = std::stoi(timestamp.substr(5, 2));
+ tm.tm_mday = std::stoi(timestamp.substr(8, 2));
+ tm.tm_hour = std::stoi(timestamp.substr(11, 2));
+ tm.tm_min = std::stoi(timestamp.substr(14, 2));
+ tm.tm_sec = std::stoi(timestamp.substr(17, 2));
+ // Auto detect daylight savings time
+ tm.tm_isdst = -1;
+ std::time_t ctime = std::mktime(&tm);
+ return clock::from_time_t(ctime);
+}
+
+std::string durationTo_hh_mm_ss(clock::duration duration)
+{
+ using namespace std::chrono;
+ if (duration > hours(24))
+ {
+ throw HueException(CURRENT_FILE_INFO, "Duration parameter longer than 1 day");
+ }
+ int numH = static_cast(duration_cast(duration).count());
+ duration -= hours(numH);
+ int numM = static_cast(duration_cast(duration).count());
+ duration -= minutes(numM);
+ int numS = static_cast(duration_cast(duration).count());
+
+ char result[9];
+ std::sprintf(result, "%02d:%02d:%02d", numH, numM, numS);
+ return std::string(result);
+}
+
+clock::duration parseDuration(const std::string& s)
+{
+ using namespace std::chrono;
+ return hours(std::stoi(s.substr(0, 2))) + minutes(std::stoi(s.substr(3, 2))) + seconds(std::stoi(s.substr(7, 2)));
+}
+
+AbsoluteTime::AbsoluteTime(clock::time_point baseTime, clock::duration variation) : base(baseTime), variation(variation)
+{}
+
+clock::time_point AbsoluteTime::getBaseTime()
+{
+ return base;
+}
+clock::duration AbsoluteTime::getRandomVariation()
+{
+ return variation;
+}
+std::string AbsoluteTime::toString()
+{
+ std::string result = timepointToTimestamp(base);
+ if (variation.count() != 0)
+ {
+ result.push_back('A');
+ result.append(durationTo_hh_mm_ss(variation));
+ }
+ return result;
+}
+
+bool Weekdays::isNone() const
+{
+ return bitmask == 0;
+}
+
+bool Weekdays::isAll() const
+{
+ // Check all 7 bits are set
+ return bitmask == (1 << 7) - 1;
+}
+
+bool Weekdays::isMonday() const
+{
+ return (bitmask & 1) != 0;
+}
+
+bool Weekdays::isTuesday() const
+{
+ return (bitmask & 2) != 0;
+}
+
+bool Weekdays::isWednesday() const
+{
+ return (bitmask & 4) != 0;
+}
+
+bool Weekdays::isThursday() const
+{
+ return (bitmask & 8) != 0;
+}
+
+bool Weekdays::isFriday() const
+{
+ return (bitmask & 16) != 0;
+}
+
+bool Weekdays::isSaturday() const
+{
+ return (bitmask & 32) != 0;
+}
+
+bool Weekdays::isSunday() const
+{
+ return (bitmask & 64) != 0;
+}
+
+std::string Weekdays::toString() const
+{
+ std::string result = std::to_string(bitmask);
+ if (result.size() < 3)
+ {
+ result.insert(0, 3 - result.size(), '0');
+ }
+ return result;
+}
+
+Weekdays Weekdays::unionWith(Weekdays other) const
+{
+ other.bitmask |= bitmask;
+ return other;
+}
+
+Weekdays Weekdays::none()
+{
+ return Weekdays();
+}
+
+Weekdays Weekdays::all()
+{
+ Weekdays result;
+ result.bitmask = (1 << 7) - 1;
+ return result;
+}
+
+Weekdays Weekdays::monday()
+{
+ return Weekdays(0);
+}
+
+Weekdays Weekdays::tuesday()
+{
+ return Weekdays(1);
+}
+
+Weekdays Weekdays::wednesday()
+{
+ return Weekdays(2);
+}
+
+Weekdays Weekdays::thursday()
+{
+ return Weekdays(3);
+}
+
+Weekdays Weekdays::friday()
+{
+ return Weekdays(4);
+}
+
+Weekdays Weekdays::saturday()
+{
+ return Weekdays(5);
+}
+
+Weekdays Weekdays::sunday()
+{
+ return Weekdays(6);
+}
+
+Weekdays Weekdays::parse(const std::string& s)
+{
+ Weekdays result;
+ result.bitmask = std::stoi(s);
+ return result;
+}
+
+RecurringTime::RecurringTime(clock::duration daytime, Weekdays days, clock::duration variation)
+ : time(daytime), days(days), variation(variation)
+{}
+
+clock::duration RecurringTime::getDaytime() const
+{
+ return time;
+}
+
+clock::duration RecurringTime::getRandomVariation() const
+{
+ return variation;
+}
+
+Weekdays RecurringTime::getWeekdays() const
+{
+ return days;
+}
+
+std::string RecurringTime::toString() const
+{
+ std::string result = "W";
+ result.append(days.toString());
+ result.append("/");
+ result.append(durationTo_hh_mm_ss(time));
+ if (variation.count() != 0)
+ {
+ result.push_back('A');
+ result.append(durationTo_hh_mm_ss(variation));
+ }
+ return std::string();
+}
+
+TimeInterval::TimeInterval(clock::duration start, clock::duration end, Weekdays days)
+ : start(start), end(end), days(days)
+{}
+
+clock::duration TimeInterval::getStartTime() const
+{
+ return start;
+}
+
+clock::duration TimeInterval::getEndTime() const
+{
+ return end;
+}
+
+Weekdays TimeInterval::getWeekdays() const
+{
+ return days;
+}
+
+std::string TimeInterval::toString() const
+{
+ std::string result;
+ if (!days.isAll())
+ {
+ result.append("W");
+ result.append(days.toString());
+ result.append("/");
+ }
+ result.push_back('T');
+ result.append(durationTo_hh_mm_ss(start));
+ result.append("/T");
+ result.append(durationTo_hh_mm_ss(end));
+
+ return result;
+}
+
+Timer::Timer(clock::duration duration, clock::duration variation)
+ : expires(duration), numExecutions(1), variation(variation)
+{}
+
+Timer::Timer(clock::duration duration, int numExecutions, clock::duration variation)
+ : expires(duration), numExecutions(numExecutions), variation(variation)
+{}
+
+bool Timer::isRecurring() const
+{
+ return numExecutions != 1;
+}
+
+int Timer::getNumberOfExecutions() const
+{
+ return numExecutions;
+}
+
+clock::duration Timer::getExpiryTime() const
+{
+ return expires;
+}
+
+clock::duration Timer::getRandomVariation() const
+{
+ return variation;
+}
+
+std::string Timer::toString() const
+{
+ std::string result;
+ if (numExecutions != 1)
+ {
+ result.push_back('R');
+ if (numExecutions != 0)
+ {
+ std::string s = std::to_string(numExecutions);
+ // Pad to two digits
+ if (s.size() < 2)
+ {
+ result.push_back('0');
+ }
+ result.append(s);
+ }
+ result.push_back('/');
+ }
+ result.append("PT");
+ result.append(durationTo_hh_mm_ss(expires));
+ if (variation.count() != 0)
+ {
+ result.push_back('A');
+ result.append(durationTo_hh_mm_ss(variation));
+ }
+ return result;
+}
+
+TimePattern::TimePattern() : type(Type::undefined), undefined(nullptr) {}
+
+TimePattern::~TimePattern()
+{
+ destroy();
+}
+
+TimePattern::TimePattern(const AbsoluteTime& absolute) : type(Type::absolute)
+{
+ new (&this->absolute) AbsoluteTime(absolute);
+}
+
+TimePattern::TimePattern(const RecurringTime& recurring) : type(Type::recurring)
+{
+ new (&this->recurring) RecurringTime(recurring);
+}
+
+TimePattern::TimePattern(const TimeInterval& interval) : type(Type::interval)
+{
+ new (&this->interval) TimeInterval(interval);
+}
+
+TimePattern::TimePattern(const Timer& timer) : type(Type::timer)
+{
+ new (&this->timer) Timer(timer);
+}
+
+TimePattern::TimePattern(const TimePattern& other) : type(Type::undefined), undefined(nullptr)
+{
+ *this = other;
+}
+
+TimePattern& TimePattern::operator=(const TimePattern& other)
+{
+ if (this == &other)
+ {
+ return *this;
+ }
+ destroy();
+ try
+ {
+ type = other.type;
+ switch (type)
+ {
+ case Type::undefined:
+ undefined = nullptr;
+ break;
+ case Type::absolute:
+ new (&absolute) AbsoluteTime(other.absolute);
+ break;
+ case Type::recurring:
+ new (&recurring) RecurringTime(other.recurring);
+ break;
+ case Type::interval:
+ new (&interval) TimeInterval(other.interval);
+ break;
+ case Type::timer:
+ new (&timer) Timer(other.timer);
+ break;
+ }
+ }
+ catch (...)
+ {
+ // Catch any throws from constructors to stay in valid state
+ type = Type::undefined;
+ undefined = nullptr;
+ throw;
+ }
+ return *this;
+}
+
+TimePattern::Type TimePattern::getType() const
+{
+ return type;
+}
+
+AbsoluteTime TimePattern::asAbsolute() const
+{
+ return absolute;
+}
+
+RecurringTime TimePattern::asRecurring() const
+{
+ return recurring;
+}
+
+TimeInterval TimePattern::asInterval() const
+{
+ return interval;
+}
+
+Timer TimePattern::asTimer() const
+{
+ return timer;
+}
+
+TimePattern TimePattern::parse(const std::string& s)
+{
+ if (s.empty() || s == "none")
+ {
+ return TimePattern();
+ }
+ else if (std::isdigit(s.front()))
+ {
+ // Absolute time
+ clock::time_point time = parseTimestamp(s);
+ clock::duration variation {0};
+ if (s.size() > 19 && s[19] == 'A')
+ {
+ // Random variation
+ variation = parseDuration(s.substr(20));
+ }
+ return TimePattern(AbsoluteTime(time, variation));
+ }
+ else if (s.front() == 'R' || s.front() == 'P')
+ {
+ // (Recurring) timer
+ int numRepetitions = 1;
+ if (s.front() == 'R')
+ {
+ if (s.at(1) == '/')
+ {
+ // Infinite
+ numRepetitions = -1;
+ }
+ else
+ {
+ numRepetitions = std::stoi(s.substr(1, 2));
+ }
+ }
+ std::size_t start = s.find('T') + 1;
+ std::size_t randomStart = s.find('A');
+ clock::duration expires = parseDuration(s.substr(start, randomStart - start));
+ clock::duration variance = std::chrono::seconds(0);
+ if (randomStart != std::string::npos)
+ {
+ variance = parseDuration(s.substr(randomStart));
+ }
+ return TimePattern(Timer(expires, numRepetitions, variance));
+ }
+ else if (s.front() == 'W' && std::count(s.begin(), s.end(), '/') == 1)
+ {
+ // Recurring time
+ Weekdays days = Weekdays::parse(s.substr(1, 3));
+ clock::duration time = parseDuration(s.substr(6));
+ clock::duration variation {0};
+ if (s.size() > 14)
+ {
+ variation = parseDuration(s.substr(15));
+ }
+ return TimePattern(RecurringTime(time, days, variation));
+ }
+ else if (s.front() == 'T' || s.front() == 'W')
+ {
+ Weekdays days = Weekdays::all();
+ if (s.front() == 'W')
+ {
+ // Time interval with weekdays
+ days = Weekdays::parse(s.substr(1, 3));
+ }
+ // Time interval
+ std::size_t start = s.find('T') + 1;
+ std::size_t end = s.find('/', start);
+ clock::duration startTime = parseDuration(s.substr(start, end - start));
+ clock::duration endTime = parseDuration(s.substr(end + 2));
+ return TimePattern(TimeInterval(startTime, endTime, days));
+ }
+ throw HueException(CURRENT_FILE_INFO, "Unable to parse time string: " + s);
+}
+
+void TimePattern::destroy()
+{
+ switch (type)
+ {
+ case Type::absolute:
+ absolute.~AbsoluteTime();
+ break;
+ case Type::recurring:
+ recurring.~RecurringTime();
+ break;
+ case Type::interval:
+ interval.~TimeInterval();
+ break;
+ case Type::timer:
+ timer.~Timer();
+ break;
+ default:
+ break;
+ }
+ type = Type::undefined;
+ undefined = nullptr;
+}
+
+} // namespace time
+} // namespace hueplusplus
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index c251d86..a498282 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -44,7 +44,8 @@ set(TEST_SOURCES
test_SimpleColorHueStrategy.cpp
test_SimpleColorTemperatureStrategy.cpp
test_UPnP.cpp
- test_StateTransaction.cpp)
+ test_StateTransaction.cpp
+ test_TimePattern.cpp)
set(HuePlusPlus_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include")
diff --git a/test/test_TimePattern.cpp b/test/test_TimePattern.cpp
new file mode 100644
index 0000000..5aede3d
--- /dev/null
+++ b/test/test_TimePattern.cpp
@@ -0,0 +1,130 @@
+/**
+ \file test_TimePattern.cpp
+ Copyright Notice\n
+ Copyright (C) 2020 Jan Rogall - developer\n
+
+ This file is part of hueplusplus.
+
+ hueplusplus is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ hueplusplus is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with hueplusplus. If not, see .
+**/
+
+#include
+#include
+
+using namespace hueplusplus::time;
+using std::chrono::system_clock;
+using namespace std::chrono_literals;
+
+TEST(AbsoluteTime, Constructor)
+{
+ system_clock::time_point now = system_clock::now();
+ {
+ AbsoluteTime time(now);
+ EXPECT_EQ(now, time.getBaseTime());
+ EXPECT_EQ(0s, time.getRandomVariation());
+ }
+ system_clock::duration variation = 4h + 2min;
+ {
+ AbsoluteTime time(now, variation);
+ EXPECT_EQ(now, time.getBaseTime());
+ EXPECT_EQ(variation, time.getRandomVariation());
+ }
+}
+
+TEST(AbsoluteTime, toString)
+{
+ std::tm time {};
+ time.tm_year = 2020 - 1900;
+ time.tm_mon = 2;
+ time.tm_mday = 3;
+ time.tm_hour = 20;
+ time.tm_min = 53;
+ time.tm_sec = 3;
+ std::time_t ctime = std::mktime(&time);
+ const system_clock::time_point timePoint = system_clock::from_time_t(ctime);
+
+ EXPECT_EQ("2020-03-03T20:53:03", AbsoluteTime(timePoint).toString());
+
+ const system_clock::duration noVariation = 0s;
+ EXPECT_EQ("2020-03-03T20:53:03", AbsoluteTime(timePoint, noVariation).toString());
+
+ const system_clock::duration variation = 1h + 2min + 1s;
+ EXPECT_EQ("2020-03-03T20:53:03A01:02:01", AbsoluteTime(timePoint, variation).toString());
+}
+
+TEST(Weekdays, Constructor)
+{
+ EXPECT_TRUE(Weekdays().isNone());
+ EXPECT_TRUE(Weekdays(0).isMonday());
+ EXPECT_TRUE(Weekdays(6).isSunday());
+}
+
+TEST(Weekdays, isXXX)
+{
+ Weekdays none = Weekdays::none();
+ EXPECT_TRUE(none.isNone());
+ EXPECT_FALSE(none.isAll());
+ EXPECT_FALSE(none.isMonday());
+ EXPECT_FALSE(none.isTuesday());
+ EXPECT_FALSE(none.isWednesday());
+ EXPECT_FALSE(none.isThursday());
+ EXPECT_FALSE(none.isFriday());
+ EXPECT_FALSE(none.isSaturday());
+ EXPECT_FALSE(none.isSunday());
+
+ Weekdays all = Weekdays::all();
+ EXPECT_FALSE(all.isNone());
+ EXPECT_TRUE(all.isAll());
+ EXPECT_TRUE(all.isMonday());
+ EXPECT_TRUE(all.isTuesday());
+ EXPECT_TRUE(all.isWednesday());
+ EXPECT_TRUE(all.isThursday());
+ EXPECT_TRUE(all.isFriday());
+ EXPECT_TRUE(all.isSaturday());
+ EXPECT_TRUE(all.isSunday());
+
+ // Test that for all days, only their own isXXX function is true
+ std::vector days {Weekdays::monday(), Weekdays::tuesday(), Weekdays::wednesday(), Weekdays::thursday(),
+ Weekdays::friday(), Weekdays::saturday(), Weekdays::sunday()};
+ using BoolGetter = bool (Weekdays::*)() const;
+ std::vector getters {&Weekdays::isMonday, &Weekdays::isTuesday, &Weekdays::isWednesday,
+ &Weekdays::isThursday, &Weekdays::isFriday, &Weekdays::isSaturday, &Weekdays::isSunday};
+ for (int i = 0; i < days.size(); ++i)
+ {
+ Weekdays day = days[i];
+ EXPECT_FALSE(day.isNone());
+ EXPECT_FALSE(day.isAll());
+ for (int j = 0; j < getters.size(); ++j)
+ {
+ EXPECT_EQ(j == i, (day.*getters[j])()) << "on Day " << i << ": getter for day " << j << " has wrong result";
+ }
+ }
+}
+
+TEST(Weekdays, unionWith)
+{
+ Weekdays day = Weekdays::monday().unionWith(Weekdays::saturday());
+ EXPECT_TRUE(day.isMonday());
+ EXPECT_TRUE(day.isSaturday());
+
+ day = Weekdays::monday() | Weekdays::tuesday() | Weekdays::all();
+ EXPECT_TRUE(day.isAll());
+}
+
+TEST(Weekdays, toString)
+{
+ EXPECT_EQ("001", Weekdays(0).toString());
+ EXPECT_EQ("064", Weekdays(6).toString());
+ EXPECT_EQ("112", (Weekdays(6) | Weekdays(5) | Weekdays(4)).toString());
+}