diff --git a/include/hueplusplus/TimePattern.h b/include/hueplusplus/TimePattern.h index 9a3f051..a69d5a9 100644 --- a/include/hueplusplus/TimePattern.h +++ b/include/hueplusplus/TimePattern.h @@ -55,7 +55,6 @@ std::chrono::system_clock::time_point parseTimestamp(const std::string& timestam //! \throws HueException when time cannot be represented as time_point std::chrono::system_clock::time_point parseUTCTimestamp(const std::string& timestamp); - //! \brief Converts duration to a time string //! \param duration Duration or time of day to format. Must be less than 24 hours //! \returns Duration string in the format hh:mm:ss @@ -68,10 +67,7 @@ std::string durationTo_hh_mm_ss(std::chrono::system_clock::duration duration); //! \throws std::invalid_argument when integer conversion fails std::chrono::system_clock::duration parseDuration(const std::string& hourMinSec); -//! \brief One-time, absolute time point with possible random variation -//! -//! Can be either used to represent a specific date and time, -//! or a date and time with a random variation. +//! \brief One-time, absolute time point class AbsoluteTime { using clock = std::chrono::system_clock; @@ -79,15 +75,46 @@ class AbsoluteTime public: //! \brief Create absolute time point //! \param baseTime Absolute time point - //! \param variation Random variation, optional. When not zero, the time is randomly chosen between - //! baseTime - variation and baseTime + variation - explicit AbsoluteTime(clock::time_point baseTime, clock::duration variation = std::chrono::seconds(0)); + explicit AbsoluteTime(clock::time_point baseTime); //! \brief Get base time point //! //! Can be used for calculation with other system_clock time_points clock::time_point getBaseTime() const; + //! \brief Get formatted string as expected by Hue API + //! \returns Timestamp in the format + //! YYYY-MM-DDThh:mm:ss + std::string toString() const; + + //! \brief Parse AbsoluteTime from formatted string in local timezone + //! \param s Timestamp in the same format as returned by \ref toString() + //! \returns AbsoluteTime with base time and variation from \c s + static AbsoluteTime parse(const std::string& s); + + //! \brief Parse AbsoluteTime from formatted UTC string + //! \param s Timestamp in the same format as returned by \ref toString() + //! \returns AbsoluteTime with base time and variation from \c s + static AbsoluteTime parseUTC(const std::string& s); + +private: + clock::time_point base; +}; +//! One-time, absolute time point with possible random variation +//! +//! Can be either used to represent a specific date and time, +//! or a date and time with a random variation. +class AbsoluteVariedTime : public AbsoluteTime +{ + using clock = std::chrono::system_clock; + +public: + //! \brief Create absolute time point + //! \param baseTime Absolute time point + //! \param variation Random variation, optional. When not zero, the time is randomly chosen between + //! baseTime - variation and baseTime + variation + explicit AbsoluteVariedTime(clock::time_point baseTime, clock::duration variation = std::chrono::seconds(0)); + //! \brief Get random variation or zero //! //! The time can vary up to this amount in both directions. @@ -103,15 +130,10 @@ public: //! \brief Parse AbsoluteTime from formatted string in local timezone //! \param s Timestamp in the same format as returned by \ref toString() - //! \returns AbsoluteTime with base time and variation from \c s - static AbsoluteTime parse(const std::string& s); + //! \returns AbsoluteVariedTime with base time and variation from \c s + static AbsoluteVariedTime parse(const std::string& s); - //! \brief Parse AbsoluteTime from formatted UTC string - //! \param s Timestamp in the same format as returned by \ref toString() - //! \returns AbsoluteTime with base time and variation from \c s - static AbsoluteTime parseUTC(const std::string& s); private: - clock::time_point base; clock::duration variation; }; @@ -122,10 +144,10 @@ class Weekdays { public: //! \brief Create with no days - Weekdays() : bitmask(0) {} + Weekdays() : bitmask(0) { } //! \brief Create with the day \c num //! \param num Day of the week, from monday (0) to sunday (6) - explicit Weekdays(int num) : bitmask(1 << num) {} + explicit Weekdays(int num) : bitmask(1 << num) { } //! \brief Check if no days are set bool isNone() const; @@ -332,7 +354,7 @@ public: enum class Type { undefined, //!< \brief No active type - absolute, //!< \brief Active type is AbsoluteTime + absolute, //!< \brief Active type is AbsoluteVariedTime recurring, //!< \brief Active type is RecurringTime interval, //!< \brief Active type is TimeInterval timer //!< \brief Active type is Timer @@ -342,8 +364,8 @@ public: TimePattern(); //! \brief Destructor for union. ~TimePattern(); - //! \brief Create TimePattern from AbsoluteTime - explicit TimePattern(const AbsoluteTime& absolute); + //! \brief Create TimePattern from AbsoluteVariedTime + explicit TimePattern(const AbsoluteVariedTime& absolute); //! \brief Create TimePattern from RecurringTime explicit TimePattern(const RecurringTime& recurring); //! \brief Create TimePattern from TimeInterval @@ -364,7 +386,7 @@ public: //! \brief Get contained absolute time //! \pre getType() == Type::absolute - AbsoluteTime asAbsolute() const; + AbsoluteVariedTime asAbsolute() const; //! \brief Get contained recurring time //! \pre getType() == Type::recurring @@ -401,7 +423,7 @@ private: union { nullptr_t undefined; - AbsoluteTime absolute; + AbsoluteVariedTime absolute; RecurringTime recurring; TimeInterval interval; Timer timer; diff --git a/src/TimePattern.cpp b/src/TimePattern.cpp index 3622afa..4c54e3d 100644 --- a/src/TimePattern.cpp +++ b/src/TimePattern.cpp @@ -143,52 +143,62 @@ system_clock::duration parseDuration(const std::string& s) return hour + min + sec; } -AbsoluteTime::AbsoluteTime(clock::time_point baseTime, clock::duration variation) : base(baseTime), variation(variation) -{} +AbsoluteTime::AbsoluteTime(clock::time_point baseTime) : base(baseTime) { } system_clock::time_point AbsoluteTime::getBaseTime() const { return base; } -system_clock::duration AbsoluteTime::getRandomVariation() const -{ - return variation; -} std::string AbsoluteTime::toString() const { - std::string result = timepointToTimestamp(base); - if (variation.count() != 0) - { - result.push_back('A'); - result.append(durationTo_hh_mm_ss(variation)); - } - return result; + return timepointToTimestamp(base); } AbsoluteTime AbsoluteTime::parse(const std::string& s) { // 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 AbsoluteTime(time, variation); + return AbsoluteTime(time); } AbsoluteTime AbsoluteTime::parseUTC(const std::string& s) { // Absolute time clock::time_point time = parseUTCTimestamp(s); + return AbsoluteTime(time); +} + +AbsoluteVariedTime::AbsoluteVariedTime(clock::time_point baseTime, clock::duration variation) + : AbsoluteTime(baseTime), variation(variation) +{ } + +system_clock::duration AbsoluteVariedTime::getRandomVariation() const +{ + return variation; +} + +AbsoluteVariedTime AbsoluteVariedTime::parse(const std::string& s) +{ + // 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 AbsoluteTime(time, variation); + return AbsoluteVariedTime(time, variation); +} + +std::string AbsoluteVariedTime::toString() const +{ + std::string result = timepointToTimestamp(getBaseTime()); + if (variation.count() != 0) + { + result.push_back('A'); + result.append(durationTo_hh_mm_ss(variation)); + } + return result; } bool Weekdays::isNone() const @@ -309,7 +319,7 @@ Weekdays Weekdays::parse(const std::string& s) RecurringTime::RecurringTime(clock::duration daytime, Weekdays days, clock::duration variation) : time(daytime), variation(variation), days(days) -{} +{ } system_clock::duration RecurringTime::getDaytime() const { @@ -342,7 +352,7 @@ std::string RecurringTime::toString() const TimeInterval::TimeInterval(clock::duration start, clock::duration end, Weekdays days) : start(start), end(end), days(days) -{} +{ } system_clock::duration TimeInterval::getStartTime() const { @@ -378,11 +388,11 @@ std::string TimeInterval::toString() const Timer::Timer(clock::duration duration, clock::duration variation) : expires(duration), variation(variation), numExecutions(1) -{} +{ } Timer::Timer(clock::duration duration, int numExecutions, clock::duration variation) : expires(duration), variation(variation), numExecutions(numExecutions) -{} +{ } bool Timer::isRecurring() const { @@ -432,20 +442,20 @@ std::string Timer::toString() const return result; } -TimePattern::TimePattern() : type(Type::undefined), undefined(nullptr) {} +TimePattern::TimePattern() : type(Type::undefined), undefined(nullptr) { } TimePattern::~TimePattern() { destroy(); } -TimePattern::TimePattern(const AbsoluteTime& absolute) : type(Type::absolute), absolute(absolute) {} +TimePattern::TimePattern(const AbsoluteVariedTime& absolute) : type(Type::absolute), absolute(absolute) { } -TimePattern::TimePattern(const RecurringTime& recurring) : type(Type::recurring), recurring(recurring) {} +TimePattern::TimePattern(const RecurringTime& recurring) : type(Type::recurring), recurring(recurring) { } -TimePattern::TimePattern(const TimeInterval& interval) : type(Type::interval), interval(interval) {} +TimePattern::TimePattern(const TimeInterval& interval) : type(Type::interval), interval(interval) { } -TimePattern::TimePattern(const Timer& timer) : type(Type::timer), timer(timer) {} +TimePattern::TimePattern(const Timer& timer) : type(Type::timer), timer(timer) { } TimePattern::TimePattern(const TimePattern& other) : type(Type::undefined), undefined(nullptr) { @@ -496,7 +506,7 @@ TimePattern::Type TimePattern::getType() const return type; } -AbsoluteTime TimePattern::asAbsolute() const +AbsoluteVariedTime TimePattern::asAbsolute() const { return absolute; } @@ -543,7 +553,7 @@ TimePattern TimePattern::parse(const std::string& s) } else if (std::isdigit(s.front())) { - return TimePattern(AbsoluteTime::parse(s)); + return TimePattern(AbsoluteVariedTime::parse(s)); } else if (s.front() == 'R' || s.front() == 'P') { @@ -606,7 +616,7 @@ void TimePattern::destroy() switch (type) { case Type::absolute: - absolute.~AbsoluteTime(); + absolute.~AbsoluteVariedTime(); break; case Type::recurring: recurring.~RecurringTime(); diff --git a/test/test_Scene.cpp b/test/test_Scene.cpp index 3be19c9..96d06de 100644 --- a/test/test_Scene.cpp +++ b/test/test_Scene.cpp @@ -272,7 +272,7 @@ TEST_F(SceneTest, getLastUpdated) expectGetState(id); const Scene scene(id, commands, std::chrono::seconds(0)); const time::AbsoluteTime lastUpdated = scene.getLastUpdated(); - EXPECT_EQ(std::chrono::seconds(0), lastUpdated.getRandomVariation()); + EXPECT_EQ(time::parseUTCTimestamp("2020-04-23T12:00:04"), lastUpdated.getBaseTime()); } TEST_F(SceneTest, getVersion) diff --git a/test/test_Schedule.cpp b/test/test_Schedule.cpp index 9d8c440..c10d5ac 100644 --- a/test/test_Schedule.cpp +++ b/test/test_Schedule.cpp @@ -239,7 +239,7 @@ TEST_F(ScheduleTest, setTime) const int id = 1; expectGetState(id); Schedule schedule(id, commands, std::chrono::steady_clock::duration::max()); - time::TimePattern time {time::AbsoluteTime(std::chrono::system_clock::now())}; + time::TimePattern time {time::AbsoluteVariedTime(std::chrono::system_clock::now())}; nlohmann::json request = {{"localtime", time.toString()}}; nlohmann::json response = {{"success", {"/schedules/1/localtime", time.toString()}}}; EXPECT_CALL( @@ -320,7 +320,7 @@ TEST(CreateSchedule, setCommand) TEST(CreateSchedule, setTime) { - const time::AbsoluteTime time(std::chrono::system_clock::now()); + const time::AbsoluteVariedTime time(std::chrono::system_clock::now()); const nlohmann::json request = {{"localtime", time.toString()}}; EXPECT_EQ(request, CreateSchedule().setTime(time::TimePattern(time)).getRequest()); } diff --git a/test/test_TimePattern.cpp b/test/test_TimePattern.cpp index 1bef75c..fc2df23 100644 --- a/test/test_TimePattern.cpp +++ b/test/test_TimePattern.cpp @@ -78,33 +78,33 @@ TEST(Time, durationTo_hh_mm_ss) EXPECT_EQ(duration, parseDuration(durationTo_hh_mm_ss(duration))); } -TEST(AbsoluteTime, Constructor) +TEST(AbsoluteVariedTime, Constructor) { system_clock::time_point now = system_clock::now(); { - AbsoluteTime time(now); + AbsoluteVariedTime time(now); EXPECT_EQ(now, time.getBaseTime()); EXPECT_EQ(0s, time.getRandomVariation()); } system_clock::duration variation = 4h + 2min; { - AbsoluteTime time(now, variation); + AbsoluteVariedTime time(now, variation); EXPECT_EQ(now, time.getBaseTime()); EXPECT_EQ(variation, time.getRandomVariation()); } } -TEST(AbsoluteTime, toString) +TEST(AbsoluteVariedTime, toString) { const system_clock::time_point timePoint = parseTimestamp("2020-03-03T20:53:03"); - EXPECT_EQ("2020-03-03T20:53:03", AbsoluteTime(timePoint).toString()); + EXPECT_EQ("2020-03-03T20:53:03", AbsoluteVariedTime(timePoint).toString()); const system_clock::duration noVariation = 0s; - EXPECT_EQ("2020-03-03T20:53:03", AbsoluteTime(timePoint, noVariation).toString()); + EXPECT_EQ("2020-03-03T20:53:03", AbsoluteVariedTime(timePoint, noVariation).toString()); const system_clock::duration variation = 1h + 2min + 1s; - EXPECT_EQ("2020-03-03T20:53:03A01:02:01", AbsoluteTime(timePoint, variation).toString()); + EXPECT_EQ("2020-03-03T20:53:03A01:02:01", AbsoluteVariedTime(timePoint, variation).toString()); } TEST(AbsoluteTime, parseUTC) @@ -361,7 +361,7 @@ TEST(TimePattern, CopyConstructor) EXPECT_EQ(TimePattern::Type::undefined, copy.getType()); } { - const AbsoluteTime abs(system_clock::now()); + const AbsoluteVariedTime abs(system_clock::now()); const TimePattern pattern(abs); const TimePattern copy(pattern); ASSERT_EQ(TimePattern::Type::absolute, copy.getType()); @@ -399,7 +399,7 @@ TEST(TimePattern, CopyConstructor) TEST(TimePattern, Absolute) { { - const AbsoluteTime abs(system_clock::now(), 20s); + const AbsoluteVariedTime abs(system_clock::now(), 20s); const TimePattern pattern(abs); ASSERT_EQ(TimePattern::Type::absolute, pattern.getType()); EXPECT_EQ(abs.getBaseTime(), pattern.asAbsolute().getBaseTime()); @@ -409,7 +409,7 @@ TEST(TimePattern, Absolute) const system_clock::time_point timePoint = parseTimestamp("2020-03-03T20:53:03"); { const TimePattern pattern = TimePattern::parse("2020-03-03T20:53:03"); - const AbsoluteTime expected(timePoint); + const AbsoluteVariedTime expected(timePoint); ASSERT_EQ(TimePattern::Type::absolute, pattern.getType()); EXPECT_EQ(expected.getBaseTime(), pattern.asAbsolute().getBaseTime()); EXPECT_EQ(expected.getRandomVariation(), pattern.asAbsolute().getRandomVariation()); @@ -417,7 +417,7 @@ TEST(TimePattern, Absolute) { const system_clock::duration variation = 1h + 2min + 1s; const TimePattern pattern = TimePattern::parse("2020-03-03T20:53:03A01:02:01"); - const AbsoluteTime expected(timePoint, variation); + const AbsoluteVariedTime expected(timePoint, variation); ASSERT_EQ(TimePattern::Type::absolute, pattern.getType()); EXPECT_EQ(expected.getBaseTime(), pattern.asAbsolute().getBaseTime()); EXPECT_EQ(expected.getRandomVariation(), pattern.asAbsolute().getRandomVariation());