M module-utils/rrule/CMakeLists.txt => module-utils/rrule/CMakeLists.txt +1 -0
@@ 13,6 13,7 @@ target_include_directories(rrule PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE
target_link_libraries(rrule
PUBLIC
date
+ time
PRIVATE
ical_cxx
)
M module-utils/rrule/rrule/rrule.cpp => module-utils/rrule/rrule/rrule.cpp +78 -15
@@ 3,6 3,8 @@
#include "rrule.hpp"
+#include <time/dateCommon.hpp>
+
extern "C"
{
#include <icalrecur.h>
@@ 22,13 24,44 @@ namespace rrule
template <typename arrayT, typename vectT>
unsigned vectorToIcalArray(const std::vector<vectT> &vect, arrayT *array, unsigned max_size);
+ RRuleIter::RRuleIter(const RRule &rrule, const TimePoint eventStart)
+ {
+ auto icalEventStart = icaltime_from_timet_with_zone(std::chrono::system_clock::to_time_t(eventStart), 0, NULL);
+ iter = icalrecur_iterator_new(RRuletoIcalRecurrenceType(rrule), icalEventStart);
+ }
+
+ RRuleIter::~RRuleIter()
+ {
+ icalrecur_iterator_free(iter);
+ iter = nullptr;
+ }
+
+ void RRuleIter::setRange(const TimePoint rangeStart, const TimePoint rangeEnd)
+ {
+ auto icalTimeStart = icaltime_from_timet_with_zone(std::chrono::system_clock::to_time_t(rangeStart), 0, NULL);
+ auto icalTimeEnd = icaltime_from_timet_with_zone(std::chrono::system_clock::to_time_t(rangeEnd), 0, NULL);
+
+ icalrecur_iterator_set_range(iter, icalTimeStart, icalTimeEnd);
+ }
+
+ TimePoint RRuleIter::next()
+ {
+ icaltimetype nextIcal = icalrecur_iterator_next(iter);
+ if (icaltime_is_null_time(nextIcal)) {
+ return TIME_POINT_INVALID;
+ }
+ else {
+ return std::chrono::system_clock::from_time_t(icaltime_as_timet(nextIcal));
+ }
+ }
+
void RRule::parseFromString(std::string_view str)
{
const auto icalRrule = icalrecurrencetype_from_string(str.data());
const time_t untilTimeT = icaltime_as_timet(icalRrule.until);
if (untilTimeT == 0) {
- until = TimePoint::min();
+ until = TIME_POINT_INVALID;
}
else {
until = std::chrono::system_clock::from_time_t(untilTimeT);
@@ 57,31 90,61 @@ namespace rrule
return std::string{icalrecurrencetype_as_string(&icalRRule)};
}
- std::vector<TimePoint> RRule::generateEventTimePoints(const TimePoint start,
- const TimePoint end,
+ std::vector<TimePoint> RRule::generateEventTimePoints(const TimePoint eventStart,
+ const TimePoint rangeStart,
+ const TimePoint rangeEnd,
const unsigned int count)
{
std::vector<TimePoint> eventsTimePoints;
- auto icalTimeStart = icaltime_from_timet_with_zone(std::chrono::system_clock::to_time_t(start), 0, NULL);
- auto icalTimeEnd = icaltime_from_timet_with_zone(std::chrono::system_clock::to_time_t(end), 0, NULL);
unsigned int counter = 0;
- const icalrecurrencetype recur = RRuletoIcalRecurrenceType(*this);
- auto ritr = icalrecur_iterator_new(recur, icalTimeStart);
- icalrecur_iterator_set_range(ritr, icalTimeStart, icalTimeEnd);
- icaltimetype next = icalrecur_iterator_next(ritr);
+ TimePoint singleEventTimePoint;
+ auto rruleIter = RRuleIter(*this, eventStart);
+ rruleIter.setRange(eventStart, rangeEnd);
- while (!icaltime_is_null_time(next) && counter < count) {
- auto singleEventTimePoint = std::chrono::system_clock::from_time_t(icaltime_as_timet(next));
- next = icalrecur_iterator_next(ritr);
+ do {
+ singleEventTimePoint = rruleIter.next();
+ if (singleEventTimePoint < rangeStart) {
+ continue;
+ }
eventsTimePoints.push_back(singleEventTimePoint);
counter++;
- }
+ } while (singleEventTimePoint != TIME_POINT_INVALID && singleEventTimePoint < rangeEnd && counter < count);
- icalrecur_iterator_free(ritr);
return eventsTimePoints;
}
+ TimePoint RRule::generateNextTimePoint(const TimePoint eventStart, const TimePoint rangeStart)
+ {
+ TimePoint singleEventTimePoint;
+ auto rruleIter = RRuleIter(*this, eventStart);
+
+ do {
+ singleEventTimePoint = rruleIter.next();
+
+ } while (singleEventTimePoint != TIME_POINT_INVALID && singleEventTimePoint < rangeStart);
+
+ return singleEventTimePoint;
+ }
+
+ TimePoint RRule::generateLastTimePoint(const TimePoint eventStart)
+ {
+ if (until == TIME_POINT_INVALID && count == 0) {
+ return TIME_POINT_MAX;
+ }
+
+ auto lastValidEventTimePoint = TIME_POINT_MAX;
+ auto singleEventTimePoint = TIME_POINT_MAX;
+ auto rruleIter = RRuleIter(*this, eventStart);
+ do {
+ lastValidEventTimePoint = singleEventTimePoint;
+ singleEventTimePoint = rruleIter.next();
+
+ } while (singleEventTimePoint != TIME_POINT_INVALID);
+
+ return lastValidEventTimePoint;
+ }
+
icalrecurrencetype RRuletoIcalRecurrenceType(const RRule &rrule)
{
icalrecurrencetype icalRrule = ICALRECURRENCETYPE_INITIALIZER;
@@ 89,7 152,7 @@ namespace rrule
icalRrule.freq = RRuleFrequencyToIcalFrequency(rrule.freq);
icalRrule.count = rrule.count;
- if (rrule.until != TimePoint::min()) {
+ if (rrule.until != TIME_POINT_INVALID) {
icalRrule.until = icaltime_from_timet_with_zone(std::chrono::system_clock::to_time_t(rrule.until), 0, NULL);
}
icalRrule.interval = rrule.interval;
M module-utils/rrule/rrule/rrule.hpp => module-utils/rrule/rrule/rrule.hpp +24 -7
@@ 1,16 1,17 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
-#include <chrono>
+#include <time/dateCommon.hpp>
+
#include <cstdint>
#include <string>
#include <vector>
+struct icalrecur_iterator_impl;
+typedef struct icalrecur_iterator_impl icalrecur_iterator;
+
namespace rrule
{
-
- using TimePoint = std::chrono::time_point<std::chrono::system_clock>;
-
class RRule
{
@@ 41,7 42,7 @@ namespace rrule
RRuleFrequency freq{RRuleFrequency::NO_RECURRENCE};
/* until and count are mutually exclusive. */
std::uint32_t count{0};
- TimePoint until{TimePoint::min()};
+ TimePoint until{TIME_POINT_INVALID};
std::uint16_t interval{0};
RRuleWeekday weekStart{RRuleWeekday::NO_WEEKDAY};
@@ 57,8 58,24 @@ namespace rrule
void parseFromString(std::string_view str);
std::string parseToString();
- std::vector<TimePoint> generateEventTimePoints(const TimePoint start,
- const TimePoint end,
+ std::vector<TimePoint> generateEventTimePoints(const TimePoint eventStart,
+ const TimePoint rangeStart,
+ const TimePoint rangeEnd,
const unsigned int count);
+ TimePoint generateNextTimePoint(const TimePoint eventStart, const TimePoint rangeStart);
+ TimePoint generateLastTimePoint(const TimePoint eventStart);
+ };
+
+ class RRuleIter
+ {
+
+ icalrecur_iterator *iter;
+
+ public:
+ RRuleIter(const RRule &rrule, const TimePoint eventStart);
+ ~RRuleIter();
+
+ void setRange(const TimePoint rangeStart, const TimePoint rangeEnd);
+ TimePoint next();
};
} // namespace rrule
M module-utils/rrule/test/unittest_rrule.cpp => module-utils/rrule/test/unittest_rrule.cpp +91 -11
@@ 33,7 33,7 @@ namespace rrule
rrule.parseFromString("FREQ=DAILY;INTERVAL=1");
REQUIRE(rrule.freq == RRule::RRuleFrequency::DAILY_RECURRENCE);
REQUIRE(rrule.count == 0);
- REQUIRE(rrule.until == TimePoint::min());
+ REQUIRE(rrule.until == TIME_POINT_INVALID);
REQUIRE(rrule.interval == 1);
REQUIRE(rrule.weekStart == RRule::RRuleWeekday::MONDAY_WEEKDAY);
REQUIRE((rrule.bySecond.empty() && rrule.byMinute.empty() && rrule.byHour.empty() &&
@@ 47,7 47,7 @@ namespace rrule
rrule.parseFromString("FREQ=MONTHLY;BYMONTHDAY=1;INTERVAL=1");
REQUIRE(rrule.freq == RRule::RRuleFrequency::MONTHLY_RECURRENCE);
REQUIRE(rrule.count == 0);
- REQUIRE(rrule.until == TimePoint::min());
+ REQUIRE(rrule.until == TIME_POINT_INVALID);
REQUIRE(rrule.interval == 1);
REQUIRE(rrule.weekStart == RRule::RRuleWeekday::MONDAY_WEEKDAY);
REQUIRE(rrule.byMonthDay.size() == 1);
@@ 76,7 76,7 @@ namespace rrule
rrule.parseFromString("FREQ=MONTHLY;BYSETPOS=4;BYDAY=TU;INTERVAL=2;COUNT=10");
REQUIRE(rrule.freq == RRule::RRuleFrequency::MONTHLY_RECURRENCE);
REQUIRE(rrule.count == 10);
- REQUIRE(rrule.until == TimePoint::min());
+ REQUIRE(rrule.until == TIME_POINT_INVALID);
REQUIRE(rrule.interval == 2);
REQUIRE(rrule.weekStart == RRule::RRuleWeekday::MONDAY_WEEKDAY);
REQUIRE(rrule.byDay.size() == 1);
@@ 144,18 144,21 @@ namespace rrule
SECTION("Generate timestamps")
{
RRule rrule;
- TimePoint start = TimePointFromString("2020-01-01 12:00:00");
- TimePoint end = TimePointFromString("2020-02-01 12:00:00");
+ TimePoint eventStart = TimePointFromString("2020-01-01 12:00:00");
+ TimePoint rangeStart = TimePointFromString("2020-01-01 12:00:00");
+ TimePoint rangeEnd = TimePointFromString("2020-02-01 12:00:00");
+
const auto GENERATE_ALL = 99999;
SECTION("Basic daily")
{
const auto teststring = "FREQ=DAILY";
- start = TimePointFromString("2020-01-01 12:00:00");
- end = TimePointFromString("2020-02-01 12:00:00");
+ eventStart = TimePointFromString("2020-01-01 12:00:00");
+ rangeStart = TimePointFromString("2020-01-01 12:00:00");
+ rangeEnd = TimePointFromString("2020-02-01 12:00:00");
rrule.parseFromString(teststring);
- auto timestamps = rrule.generateEventTimePoints(start, end, GENERATE_ALL);
+ auto timestamps = rrule.generateEventTimePoints(eventStart, rangeStart, rangeEnd, GENERATE_ALL);
REQUIRE(timestamps.size() == 31);
REQUIRE(timestamps[0] == TimePointFromString("2020-01-01 12:00:00"));
@@ 165,17 168,94 @@ namespace rrule
SECTION("Basic hourly interval count")
{
const auto teststring = "FREQ=HOURLY;INTERVAL=2;COUNT=10";
- start = TimePointFromString("2020-01-01 12:00:00");
- end = TimePointFromString("2020-02-01 12:00:00");
+ eventStart = TimePointFromString("2020-01-01 12:00:00");
+ rangeStart = TimePointFromString("2020-01-01 12:00:00");
+ rangeEnd = TimePointFromString("2020-02-01 12:00:00");
rrule.parseFromString(teststring);
- auto timestamps = rrule.generateEventTimePoints(start, end, GENERATE_ALL);
+ auto timestamps = rrule.generateEventTimePoints(eventStart, rangeStart, rangeEnd, GENERATE_ALL);
REQUIRE(timestamps.size() == 10);
REQUIRE(timestamps[0] == TimePointFromString("2020-01-01 12:00:00"));
REQUIRE(timestamps[1] == TimePointFromString("2020-01-01 14:00:00"));
REQUIRE(timestamps[9] == TimePointFromString("2020-01-02 06:00:00"));
}
+
+ SECTION("Basic hourly interval after 5 hours")
+ {
+ const auto teststring = "FREQ=HOURLY;INTERVAL=2";
+ eventStart = TimePointFromString("2020-01-01 12:00:00");
+ rangeStart = TimePointFromString("2020-01-01 16:30:00");
+ rangeEnd = TimePointFromString("2020-01-01 21:30:00");
+ rrule.parseFromString(teststring);
+
+ auto timestamps = rrule.generateEventTimePoints(eventStart, rangeStart, rangeEnd, GENERATE_ALL);
+
+ REQUIRE(timestamps.size() == 2);
+ REQUIRE(timestamps[0] == TimePointFromString("2020-01-01 18:00:00"));
+ REQUIRE(timestamps[1] == TimePointFromString("2020-01-01 20:00:00"));
+ }
+
+ SECTION("Generate Next daily")
+ {
+ const auto teststring = "FREQ=DAILY";
+ eventStart = TimePointFromString("2020-01-01 12:00:00");
+ rangeStart = TimePointFromString("2020-01-07 12:00:00");
+ rrule.parseFromString(teststring);
+
+ auto timestamp = rrule.generateNextTimePoint(eventStart, rangeStart);
+ REQUIRE(timestamp == TimePointFromString("2020-01-07 12:00:00"));
+ }
+
+ SECTION("Generate Next hourly")
+ {
+ const auto teststring = "FREQ=HOURLY;INTERVAL=2;COUNT=10";
+ eventStart = TimePointFromString("2020-01-01 12:00:00");
+ rangeStart = TimePointFromString("2020-01-01 15:30:00");
+ rrule.parseFromString(teststring);
+
+ auto timestamp = rrule.generateNextTimePoint(eventStart, rangeStart);
+ REQUIRE(timestamp == TimePointFromString("2020-01-01 16:00:00"));
+ }
+ SECTION("Generate Next daily 5 days later")
+ {
+ const auto teststring = "FREQ=DAILY;INTERVAL=5";
+ eventStart = TimePointFromString("2020-01-01 12:00:00");
+ rangeStart = TimePointFromString("2020-01-3 12:00:00");
+
+ rrule.parseFromString(teststring);
+ auto timestamp = rrule.generateNextTimePoint(eventStart, rangeStart);
+ REQUIRE(timestamp == TimePointFromString("2020-01-06 12:00:00"));
+ }
+ SECTION("Generate Last daily")
+ {
+ const auto teststring = "FREQ=DAILY;COUNT=10";
+ eventStart = TimePointFromString("2020-01-01 12:00:00");
+ rrule.parseFromString(teststring);
+
+ auto timestamp = rrule.generateLastTimePoint(eventStart);
+ REQUIRE(timestamp == TimePointFromString("2020-01-10 12:00:00"));
+ }
+
+ SECTION("Generate Last hourly")
+ {
+ const auto teststring = "FREQ=HOURLY;INTERVAL=2;COUNT=10";
+ eventStart = TimePointFromString("2020-01-01 12:00:00");
+ rrule.parseFromString(teststring);
+
+ auto timestamp = rrule.generateLastTimePoint(eventStart);
+ REQUIRE(timestamp == TimePointFromString("2020-01-02 06:00:00"));
+ }
+
+ SECTION("Generate Last intinite")
+ {
+ const auto teststring = "FREQ=HOURLY;INTERVAL=2";
+ eventStart = TimePointFromString("2020-01-01 12:00:00");
+ rrule.parseFromString(teststring);
+
+ auto timestamp = rrule.generateLastTimePoint(eventStart);
+ REQUIRE(timestamp == TIME_POINT_MAX);
+ }
}
}
} // namespace rrule
M module-utils/time/CMakeLists.txt => module-utils/time/CMakeLists.txt +1 -1
@@ 26,12 26,12 @@ target_include_directories(time PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_
target_link_libraries(time
PRIVATE
- module-utils
time-constants
utz::utz
PUBLIC
i18n
log
+ module-utils
utf8
)
M module-utils/time/time/dateCommon.hpp => module-utils/time/time/dateCommon.hpp +1 -0
@@ 100,6 100,7 @@ enum class Repeat
};
inline constexpr TimePoint TIME_POINT_INVALID = date::sys_days{date::January / 1 / 1970};
+inline constexpr TimePoint TIME_POINT_MAX = date::sys_days{date::April / 11 / 2262};
inline constexpr uint32_t yearDigitsNumb = 4, monthDigitsNumb = 2, dayDigitsNumb = 2, HourDigitsNumb = 2,
MinDigitsNumb = 2, SecDigitsNumb = 2;