~aleteoryx/muditaos

80768bf4a59e43bd9c2eb9ca4cd30562a7a4ee74 — Tomas Rogala 5 years ago b5780f8
[EGD-4708] Add backend of multiday events handling

-sql req for multiday events handling
-Add UT
-Add markEventsInDays method
-Add new query for day filter
42 files changed, 746 insertions(+), 202 deletions(-)

M module-apps/application-calendar/ApplicationCalendar.cpp
M module-apps/application-calendar/ApplicationCalendar.hpp
M module-apps/application-calendar/data/CalendarData.hpp
M module-apps/application-calendar/data/dateCommon.hpp
M module-apps/application-calendar/models/DayEventsModel.cpp
M module-apps/application-calendar/models/DayEventsModel.hpp
M module-apps/application-calendar/models/MonthModel.cpp
M module-apps/application-calendar/models/MonthModel.hpp
M module-apps/application-calendar/widgets/EventTimeItem.cpp
M module-apps/application-calendar/widgets/EventTimeItem.hpp
M module-apps/application-calendar/widgets/MonthBox.cpp
M module-apps/application-calendar/widgets/MonthBox.hpp
M module-apps/application-calendar/windows/AllEventsWindow.hpp
M module-apps/application-calendar/windows/CalendarMainWindow.cpp
M module-apps/application-calendar/windows/CalendarMainWindow.hpp
M module-apps/application-calendar/windows/DayEventsWindow.hpp
M module-db/CMakeLists.txt
M module-db/Interface/AlarmsRecord.hpp
M module-db/Interface/EventsRecord.cpp
M module-db/Interface/EventsRecord.hpp
M module-db/Tables/AlarmsTable.cpp
M module-db/Tables/AlarmsTable.hpp
M module-db/Tables/EventsTable.cpp
M module-db/Tables/EventsTable.hpp
M module-db/queries/calendar/QueryEventsGetFiltered.cpp
M module-db/queries/calendar/QueryEventsGetFiltered.hpp
A module-db/queries/calendar/QueryEventsGetFilteredByDay.cpp
A module-db/queries/calendar/QueryEventsGetFilteredByDay.hpp
M module-db/queries/calendar/QueryEventsSelectFirstUpcoming.cpp
M module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp
M module-db/tests/AlarmsRecord_tests.cpp
M module-db/tests/AlarmsTable_tests.cpp
M module-db/tests/EventsRecord_tests.cpp
M module-db/tests/EventsTable_tests.cpp
M module-services/service-time/service-time/CalendarTimeEvents.hpp
M module-services/service-time/timeEvents/CalendarTimeEvents.cpp
M module-utils/ical/ParserICS.cpp
M module-utils/ical/ParserICS.hpp
M module-utils/time/TimeRangeParser.cpp
M module-utils/time/TimeRangeParser.hpp
M module-utils/time/time_conversion.hpp
M test/pytest/service-desktop/test_calendar.py
M module-apps/application-calendar/ApplicationCalendar.cpp => module-apps/application-calendar/ApplicationCalendar.cpp +1 -1
@@ 147,7 147,7 @@ namespace app
    void ApplicationCalendar::destroyUserInterface()
    {}

    void ApplicationCalendar::switchToNoEventsWindow(const std::string &title, const calendar::TimePoint &dateFilter)
    void ApplicationCalendar::switchToNoEventsWindow(const std::string &title, const TimePoint &dateFilter)
    {
        if (equivalentWindow == EquivalentWindow::DayEventsWindow) {
            popToWindow(gui::name::window::main_window);

M module-apps/application-calendar/ApplicationCalendar.hpp => module-apps/application-calendar/ApplicationCalendar.hpp +1 -2
@@ 55,8 55,7 @@ namespace app
        }
        void createUserInterface() override;
        void destroyUserInterface() override;
        void switchToNoEventsWindow(const std::string &title              = "",
                                    const calendar::TimePoint &dateFilter = calendar::TimePoint());
        void switchToNoEventsWindow(const std::string &title = "", const TimePoint &dateFilter = TimePoint());

        static const std::map<Reminder, const char *> reminderOptions;
        static const std::map<Repeat, const char *> repeatOptions;

M module-apps/application-calendar/data/CalendarData.hpp => module-apps/application-calendar/data/CalendarData.hpp +3 -3
@@ 42,7 42,7 @@ class DayMonthData : public gui::SwitchData
{
  protected:
    std::string dayMonth;
    calendar::TimePoint dateFilter;
    TimePoint dateFilter;

  public:
    DayMonthData()          = default;


@@ 52,12 52,12 @@ class DayMonthData : public gui::SwitchData
        return dayMonth;
    };

    calendar::TimePoint getDateFilter()
    TimePoint getDateFilter()
    {
        return dateFilter;
    };

    virtual void setData(std::string dayMonthText, const calendar::TimePoint &dateNumb)
    virtual void setData(std::string dayMonthText, const TimePoint &dateNumb)
    {
        dayMonth   = dayMonthText;
        dateFilter = dateNumb;

M module-apps/application-calendar/data/dateCommon.hpp => module-apps/application-calendar/data/dateCommon.hpp +27 -26
@@ 9,15 9,16 @@
#include <Utils.hpp>
#include <random>

using Clock     = std::chrono::system_clock;
using TimePoint = std::chrono::time_point<Clock>;

namespace calendar
{
    using Clock            = std::chrono::system_clock;
    using TimePoint        = std::chrono::time_point<Clock>;
    using YearMonthDay     = date::year_month_day;
    using YearMonthDayLast = date::year_month_day_last;
} // namespace calendar

inline constexpr auto max_month_day = 48;
inline constexpr auto max_month_day = 31;

enum class Reminder
{


@@ 43,7 44,7 @@ enum class Repeat
    yearly
};

inline constexpr calendar::TimePoint TIME_POINT_INVALID = date::sys_days{date::January / 1 / 1970};
inline constexpr TimePoint TIME_POINT_INVALID = date::sys_days{date::January / 1 / 1970};
inline constexpr uint32_t yearDigitsNumb = 4, monthDigitsNumb = 2, dayDigitsNumb = 2, HourDigitsNumb = 2,
                          MinDigitsNumb = 2, SecDigitsNumb = 2;



@@ 88,34 89,34 @@ inline time_t GetAsUTCTime(int year, int month, int day, int hour = 0, int minut
    return basetime + GetDiffLocalWithUTCTime();
}

inline calendar::TimePoint TimePointFromTimeT(const time_t &time)
inline TimePoint TimePointFromTimeT(const time_t &time)
{
    return std::chrono::system_clock::from_time_t(time);
}

inline time_t TimePointToTimeT(const calendar::TimePoint &tp)
inline time_t TimePointToTimeT(const TimePoint &tp)
{
    return std::chrono::system_clock::to_time_t(tp);
}

inline calendar::TimePoint TimePointNow()
inline TimePoint TimePointNow()
{
    utils::time::Timestamp timestamp;
    return TimePointFromTimeT(timestamp.getTime());
}

inline std::string TimePointToString(const calendar::TimePoint &tp)
inline std::string TimePointToString(const TimePoint &tp)
{
    return date::format("%F %T", std::chrono::time_point_cast<std::chrono::seconds>(tp));
}

inline auto TimePointToHourMinSec(const calendar::TimePoint &tp)
inline auto TimePointToHourMinSec(const TimePoint &tp)
{
    auto dp = date::floor<date::days>(tp);
    return date::make_time(tp - dp);
}

inline uint32_t TimePointToHour24H(const calendar::TimePoint &tp)
inline uint32_t TimePointToHour24H(const TimePoint &tp)
{
    auto time = TimePointToTimeT(tp);
    utils::time::Timestamp timestamp(time);


@@ 123,7 124,7 @@ inline uint32_t TimePointToHour24H(const calendar::TimePoint &tp)
    return hour;
}

inline uint32_t TimePointToMinutes(const calendar::TimePoint &tp)
inline uint32_t TimePointToMinutes(const TimePoint &tp)
{
    auto time = TimePointToTimeT(tp);
    utils::time::Timestamp timestamp(time);


@@ 131,7 132,7 @@ inline uint32_t TimePointToMinutes(const calendar::TimePoint &tp)
    return minute;
}

inline calendar::TimePoint getFirstWeekDay(const calendar::TimePoint &tp)
inline TimePoint getFirstWeekDay(const TimePoint &tp)
{
    date::year_month_day yearMonthDay = date::year_month_day{date::floor<date::days>(tp)};
    auto hourV                        = TimePointToHour24H(tp);


@@ 146,12 147,12 @@ inline calendar::TimePoint getFirstWeekDay(const calendar::TimePoint &tp)
    return finalDateTime;
}

inline std::string TimePointToString(const calendar::TimePoint &tp, date::months months)
inline std::string TimePointToString(const TimePoint &tp, date::months months)
{
    date::year_month_day yearMonthDay     = date::year_month_day{date::floor<date::days>(tp)};
    date::year_month_day yearMonthDayLast = yearMonthDay.year() / yearMonthDay.month() / date::last;

    calendar::TimePoint timePoint;
    TimePoint timePoint;

    if ((static_cast<unsigned>(yearMonthDay.month()) + months.count()) <= 12) {
        if (yearMonthDayLast.day() == yearMonthDay.day()) {


@@ 176,38 177,38 @@ inline std::string TimePointToString(const calendar::TimePoint &tp, date::months
    return date::format("%F %T", std::chrono::time_point_cast<std::chrono::seconds>(timePoint));
}

inline std::string TimePointToLocalizedDateString(const calendar::TimePoint &tp, const std::string format = "")
inline std::string TimePointToLocalizedDateString(const TimePoint &tp, const std::string format = "")
{
    auto time = TimePointToTimeT(tp);
    utils::time::Date timestamp(time);
    return timestamp.str(format);
}

inline std::string TimePointToLocalizedTimeString(const calendar::TimePoint &tp, const std::string format = "")
inline std::string TimePointToLocalizedTimeString(const TimePoint &tp, const std::string format = "")
{
    auto time = TimePointToTimeT(tp);
    utils::time::Time timestamp(time);
    return timestamp.str(format);
}

inline calendar::TimePoint TimePointFromString(const char *s1)
inline TimePoint TimePointFromString(const char *s1)
{
    calendar::TimePoint tp;
    TimePoint tp;
    std::istringstream(s1) >> date::parse("%F %T", tp);
    return tp;
}

inline calendar::YearMonthDay TimePointToYearMonthDay(const calendar::TimePoint &tp)
inline calendar::YearMonthDay TimePointToYearMonthDay(const TimePoint &tp)
{
    return date::year_month_day{date::floor<date::days>(tp)};
}

inline calendar::TimePoint TimePointFromYearMonthDay(const calendar::YearMonthDay &ymd)
inline TimePoint TimePointFromYearMonthDay(const calendar::YearMonthDay &ymd)
{
    return date::sys_days{ymd.year() / ymd.month() / ymd.day()};
}

inline time_t TimePointToMin(const calendar::TimePoint &tp)
inline time_t TimePointToMin(const TimePoint &tp)
{
    auto time     = TimePointToTimeT(tp);
    auto duration = new utils::time::Duration(time);


@@ 215,7 216,7 @@ inline time_t TimePointToMin(const calendar::TimePoint &tp)
    return minutes;
}

inline uint32_t TimePointToHour12H(const calendar::TimePoint &tp)
inline uint32_t TimePointToHour12H(const TimePoint &tp)
{
    auto time = TimePointToTimeT(tp);
    utils::time::Timestamp timestamp(time);


@@ 226,7 227,7 @@ inline uint32_t TimePointToHour12H(const calendar::TimePoint &tp)
    return hour;
}

inline std::string TimePointToHourString12H(const calendar::TimePoint &tp)
inline std::string TimePointToHourString12H(const TimePoint &tp)
{
    auto hour =
        utils::time::Timestamp(TimePointToTimeT(tp)).get_UTC_date_time_sub_value(utils::time::GetParameters::Hour);


@@ 234,14 235,14 @@ inline std::string TimePointToHourString12H(const calendar::TimePoint &tp)
    return utils::to_string(hour12h);
}

inline std::string TimePointToHourString24H(const calendar::TimePoint &tp)
inline std::string TimePointToHourString24H(const TimePoint &tp)
{
    auto hour =
        utils::time::Timestamp(TimePointToTimeT(tp)).get_UTC_date_time_sub_value(utils::time::GetParameters::Hour);
    return utils::to_string(hour);
}

inline std::string TimePointToMinutesString(const calendar::TimePoint &tp)
inline std::string TimePointToMinutesString(const TimePoint &tp)
{
    auto minute       = TimePointToMinutes(tp);
    auto minuteString = std::to_string(minute);


@@ 252,7 253,7 @@ inline std::string TimePointToMinutesString(const calendar::TimePoint &tp)
}

// 0: Monday, 1: Tuesday ... 6: Sunday
inline unsigned int WeekdayIndexFromTimePoint(const calendar::TimePoint &tp)
inline unsigned int WeekdayIndexFromTimePoint(const TimePoint &tp)
{
    auto ymw = date::year_month_weekday{std::chrono::floor<date::days>(tp)};
    return ymw.weekday().iso_encoding() - 1;

M module-apps/application-calendar/models/DayEventsModel.cpp => module-apps/application-calendar/models/DayEventsModel.cpp +5 -4
@@ 6,7 6,7 @@
#include "application-calendar/data/CalendarData.hpp"
#include "application-calendar/ApplicationCalendar.hpp"
#include <ListView.hpp>
#include <queries/calendar/QueryEventsGetFiltered.hpp>
#include <queries/calendar/QueryEventsGetFilteredByDay.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <service-db/QueryMessage.hpp>
#include <module-db/queries/RecordQuery.hpp>


@@ 27,7 27,7 @@ unsigned int DayEventsModel::getMinimalItemHeight() const

void DayEventsModel::requestRecords(const uint32_t offset, const uint32_t limit)
{
    auto query = std::make_unique<db::query::events::GetFiltered>(filterFrom, filterTill, offset, limit);
    auto query = std::make_unique<db::query::events::GetFilteredByDay>(filterFrom, offset, limit);
    auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Events);
    task->setCallback([this](auto response) { return handleQueryResponse(response); });
    task->execute(application, this);


@@ 63,7 63,7 @@ bool DayEventsModel::updateRecords(std::vector<EventsRecord> records)

auto DayEventsModel::handleQueryResponse(db::QueryResult *queryResult) -> bool
{
    if (auto response = dynamic_cast<db::query::events::GetFilteredResult *>(queryResult); response != nullptr) {
    if (auto response = dynamic_cast<db::query::events::GetFilteredByDayResult *>(queryResult); response != nullptr) {
        if (recordsCount != (response->getCountResult())) {
            recordsCount = response->getCountResult();
            list->rebuildList(style::listview::RebuildType::Full, 0, true);


@@ 95,8 95,9 @@ auto DayEventsModel::handleQueryResponse(db::QueryResult *queryResult) -> bool
    return false;
}

void DayEventsModel::setFilters(calendar::TimePoint from, calendar::TimePoint till, const std::string &dayMonth)
void DayEventsModel::setFilters(TimePoint from, TimePoint till, const std::string &dayMonth)
{
    /// TODO: change to one filter
    this->filterFrom    = from;
    this->filterTill    = till;
    this->dayMonthTitle = dayMonth;

M module-apps/application-calendar/models/DayEventsModel.hpp => module-apps/application-calendar/models/DayEventsModel.hpp +3 -3
@@ 15,13 15,13 @@ class DayEventsModel : public app::DatabaseModel<EventsRecord>,
{
    app::Application *application = nullptr;
    std::string dayMonthTitle;
    calendar::TimePoint filterFrom = TIME_POINT_INVALID;
    calendar::TimePoint filterTill = TIME_POINT_INVALID;
    TimePoint filterFrom = TIME_POINT_INVALID;
    TimePoint filterTill = TIME_POINT_INVALID;

  public:
    DayEventsModel(app::Application *app);

    void setFilters(calendar::TimePoint from, calendar::TimePoint till, const std::string &dayMonth);
    void setFilters(TimePoint from, TimePoint till, const std::string &dayMonth);

    bool updateRecords(std::vector<EventsRecord> records) override;
    auto handleQueryResponse(db::QueryResult *) -> bool;

M module-apps/application-calendar/models/MonthModel.cpp => module-apps/application-calendar/models/MonthModel.cpp +60 -7
@@ 3,9 3,34 @@

#include "MonthModel.hpp"
#include <time/time_locale.hpp>
#include <time/time_conversion.hpp>

date::year_month_day MonthModel::getYearMonthDayFromTimePoint(TimePoint timePoint) const
{
    return date::year_month_day{date::floor<date::days>(timePoint)};
}

uint32_t MonthModel::getEventDurationInDays(const EventsRecord &record) const
{
    auto eventStartDuration = utils::time::Duration(TimePointToTimeT(record.date_from));
    auto eventEndDuration   = utils::time::Duration(TimePointToTimeT(record.date_till));
    auto startEventDurationSinceEpochInDaysRoundedDown =
        (eventStartDuration.getHours() - eventStartDuration.getHours() % utils::time::hoursInday) /
        utils::time::hoursInday;
    auto endEventDurationSinceEpochInDaysRoundedDown =
        (eventEndDuration.getHours() - eventEndDuration.getHours() % utils::time::hoursInday) / utils::time::hoursInday;
    return endEventDurationSinceEpochInDaysRoundedDown - startEventDurationSinceEpochInDaysRoundedDown;
}

uint32_t MonthModel::getDayIndex(TimePoint date) const
{
    date::year_month_day recordDate = TimePointToYearMonthDay(date);
    return (static_cast<unsigned>(recordDate.day()) - 1);
}

MonthModel::MonthModel(date::year_month_day yearMonthDay)
{
    this->yearMonth                            = YearMonth(yearMonthDay);
    this->month                                = yearMonthDay.month();
    this->year                                 = yearMonthDay.year();
    date::year_month_day_last yearMonthDayLast = this->year / this->month / date::last;


@@ 14,6 39,35 @@ MonthModel::MonthModel(date::year_month_day yearMonthDay)
    this->firstWeekDayNumb                     = date::weekday{yearMonthDayFirst}.c_encoding();
}

void MonthModel::markEventsInDays(const std::vector<EventsRecord> &records, std::array<bool, 31> &isDayEmpty)
{
    for (auto &rec : records) {
        auto eventYearMonthFrom = YearMonth(getYearMonthDayFromTimePoint(rec.date_from));
        auto eventYearMonthTill = YearMonth(getYearMonthDayFromTimePoint(rec.date_till));

        if (eventYearMonthFrom < this->yearMonth && this->yearMonth < eventYearMonthTill) {
            for (uint32_t i = 0; i < max_month_day; i++) {
                isDayEmpty[i] = false;
            }
            return;
        }

        int durationInDays = getEventDurationInDays(rec);

        if (this->yearMonth == eventYearMonthFrom) {
            for (int i = getDayIndex(rec.date_from); i < max_month_day && durationInDays >= 0; i++, durationInDays--) {
                isDayEmpty[i] = false;
            }
        }

        if (this->yearMonth == eventYearMonthTill) {
            for (int i = getDayIndex(rec.date_till); i >= 0 && durationInDays >= 0; i--, durationInDays--) {
                isDayEmpty[i] = false;
            }
        }
    }
}

date::year MonthModel::getYear()
{
    return year;


@@ 52,18 106,17 @@ std::vector<std::string> MonthModel::split(const std::string &s, char delim)

std::string MonthModel::getMonthText()
{
    unsigned int monthUInt = static_cast<unsigned>(month);
    std::string monthStr   = utils::time::Locale::get_month(utils::time::Locale::Month(monthUInt - 1));
    auto monthUInt       = static_cast<unsigned>(month);
    std::string monthStr = utils::time::Locale::get_month(utils::time::Locale::Month(monthUInt - 1));
    return monthStr;
}

std::string MonthModel::getMonthYearText()
{
    int yearUInt        = static_cast<decltype(yearUInt)>(year);
    std::string yearStr = std::to_string(yearUInt);
    std::string monthStr;
    unsigned int monthUInt = static_cast<unsigned>(month);
    monthStr               = utils::time::Locale::get_month(utils::time::Locale::Month(monthUInt - 1));
    auto yearInt         = static_cast<int>(year);
    std::string yearStr  = std::to_string(yearInt);
    auto monthUInt       = static_cast<unsigned>(month);
    std::string monthStr = utils::time::Locale::get_month(utils::time::Locale::Month(monthUInt - 1));

    return monthStr + " " + yearStr;
}

M module-apps/application-calendar/models/MonthModel.hpp => module-apps/application-calendar/models/MonthModel.hpp +51 -7
@@ 13,21 13,59 @@
#include <string>
#include <vector>
#include <module-apps/application-calendar/data/dateCommon.hpp>
#include <module-db/Interface/EventsRecord.hpp>

class MonthModel
class YearMonth
{
  public:
    date::month month;
    unsigned int lastDay;
    // first week offset
    uint32_t firstWeekDayNumb;
    date::year year;
    date::month month;

  public:
    MonthModel(date::year_month_day ymd);
    YearMonth() = default;
    explicit YearMonth(date::year_month_day ymd) : year{ymd.year()}, month{ymd.month()} {};

    bool operator>(const YearMonth &other) const noexcept
    {
        if (this->year != other.year) {
            return this->year > other.year;
        }
        else {
            return this->month > other.month;
        }
    };

    bool operator==(const YearMonth &other) const noexcept
    {
        return this->month == other.month && this->year == other.year;
    };

    bool operator!=(const YearMonth &other)
    {
        return !(*this == other);
    };

    bool operator<(const YearMonth &other)
    {
        return !(*this > other) && (*this != other);
    };
};

class MonthModel
{
  private:
    YearMonth yearMonth;

    [[nodiscard]] date::year_month_day getYearMonthDayFromTimePoint(TimePoint timePoint) const;
    [[nodiscard]] uint32_t getEventDurationInDays(const EventsRecord &records) const;
    [[nodiscard]] uint32_t getDayIndex(TimePoint date) const;

  public:
    explicit MonthModel(date::year_month_day yearMonthDay);
    MonthModel()          = default;
    virtual ~MonthModel() = default;

    void markEventsInDays(const std::vector<EventsRecord> &records, std::array<bool, 31> &isDayEmpty);

    date::year getYear();
    date::month getMonth();
    uint32_t getLastDay();


@@ 35,4 73,10 @@ class MonthModel
    std::string getMonthYearText();
    std::string getMonthText();
    std::vector<std::string> split(const std::string &s, char delim);

    date::month month;
    uint32_t lastDay;
    // first week offset
    uint32_t firstWeekDayNumb;
    date::year year;
};

M module-apps/application-calendar/widgets/EventTimeItem.cpp => module-apps/application-calendar/widgets/EventTimeItem.cpp +3 -3
@@ 433,9 433,9 @@ namespace gui
        }
    }

    calendar::TimePoint EventTimeItem::calculateEventTime(calendar::YearMonthDay date,
                                                          std::chrono::hours hours,
                                                          std::chrono::minutes minutes)
    TimePoint EventTimeItem::calculateEventTime(calendar::YearMonthDay date,
                                                std::chrono::hours hours,
                                                std::chrono::minutes minutes)
    {
        return TimePointFromYearMonthDay(date) + hours + minutes;
    }

M module-apps/application-calendar/widgets/EventTimeItem.hpp => module-apps/application-calendar/widgets/EventTimeItem.hpp +3 -3
@@ 43,9 43,9 @@ namespace gui
                                    std::chrono::minutes end_hour,
                                    uint32_t start_minutes,
                                    uint32_t end_minutes);
        calendar::TimePoint calculateEventTime(calendar::YearMonthDay date,
                                               std::chrono::hours hours,
                                               std::chrono::minutes minutes);
        TimePoint calculateEventTime(calendar::YearMonthDay date,
                                     std::chrono::hours hours,
                                     std::chrono::minutes minutes);

      public:
        EventTimeItem(const std::string &description,

M module-apps/application-calendar/widgets/MonthBox.cpp => module-apps/application-calendar/widgets/MonthBox.cpp +1 -1
@@ 16,7 16,7 @@ namespace gui
                       const uint32_t &dayWidth,
                       const uint32_t &dayHeight,
                       const std::unique_ptr<MonthModel> &model,
                       bool *isDayEmpty)
                       std::array<bool, 31> &isDayEmpty)
        : GridLayout(parent, style::window::default_left_margin, offsetTop, width, height, {dayWidth, dayHeight})
    {
        LOG_DEBUG("Call MonthBox constructor");

M module-apps/application-calendar/widgets/MonthBox.hpp => module-apps/application-calendar/widgets/MonthBox.hpp +1 -1
@@ 19,7 19,7 @@ namespace gui
                 const uint32_t &dayWidth,
                 const uint32_t &dayHeight,
                 const std::unique_ptr<MonthModel> &model,
                 bool *isDayEmpty);
                 std::array<bool, 31> &isDayEmpty);

        ~MonthBox() override = default;
        std::string month;

M module-apps/application-calendar/windows/AllEventsWindow.hpp => module-apps/application-calendar/windows/AllEventsWindow.hpp +1 -1
@@ 16,7 16,7 @@ namespace gui
        gui::Image *leftArrowImage   = nullptr;
        gui::Image *newDayEventImage = nullptr;

        calendar::TimePoint dateFilter                 = TimePointNow();
        TimePoint dateFilter                           = TimePointNow();
        gui::ListView *allEventsList                   = nullptr;
        std::shared_ptr<AllEventsModel> allEventsModel = nullptr;


M module-apps/application-calendar/windows/CalendarMainWindow.cpp => module-apps/application-calendar/windows/CalendarMainWindow.cpp +3 -8
@@ 29,12 29,12 @@ namespace gui
        buildInterface();
    }

    void CalendarMainWindow::refresh()
    void CalendarMainWindow::refresh(const std::vector<EventsRecord> &records)
    {
        erase(dateLabel);
        monthBox->erase();

        monthModel           = std::make_unique<MonthModel>(actualDate);
        monthModel->markEventsInDays(records, isDayEmpty);
        std::string dateText = monthModel->getMonthYearText();
        this->buildMonth(monthModel);
        this->buildDateLabel(dateText);


@@ 213,12 213,7 @@ namespace gui
        std::fill(std::begin(isDayEmpty), std::end(isDayEmpty), true);
        if (auto response = dynamic_cast<db::query::events::GetFilteredResult *>(queryResult)) {
            const auto records = response->getResult();
            for (auto &rec : records) {
                date::year_month_day recordDate = TimePointToYearMonthDay(rec.date_from);
                uint32_t dayNumb                = static_cast<unsigned>(recordDate.day());
                isDayEmpty[dayNumb - 1]         = false;
            }
            refresh();
            refresh(records);
            return true;
        }
        if (auto response = dynamic_cast<db::query::events::GetAllResult *>(queryResult)) {

M module-apps/application-calendar/windows/CalendarMainWindow.hpp => module-apps/application-calendar/windows/CalendarMainWindow.hpp +2 -2
@@ 24,7 24,7 @@ namespace gui

    class CalendarMainWindow : public gui::AppWindow, public app::AsyncCallbackReceiver
    {
        bool isDayEmpty[31];
        std::array<bool, 31> isDayEmpty;
        uint32_t offsetFromTop = 0;
        uint32_t monthWidth    = 0;
        uint32_t monthHeight   = 0;


@@ 41,7 41,7 @@ namespace gui
        CalendarMainWindow(app::Application *app, const std::string &name);

        void rebuild() override;
        void refresh();
        void refresh(const std::vector<EventsRecord> &records);
        void filterRequest();
        void buildMonth(std::unique_ptr<MonthModel> &model);
        void buildDateLabel(std::string actualDateTime);

M module-apps/application-calendar/windows/DayEventsWindow.hpp => module-apps/application-calendar/windows/DayEventsWindow.hpp +1 -1
@@ 19,7 19,7 @@ namespace gui
    class DayEventsWindow : public gui::AppWindow
    {
        std::string dayMonthTitle;
        calendar::TimePoint filterFrom;
        TimePoint filterFrom;
        gui::Image *leftArrowImage                               = nullptr;
        gui::Image *newDayEventImage                             = nullptr;
        gui::ListView *dayEventsList                             = nullptr;

M module-db/CMakeLists.txt => module-db/CMakeLists.txt +1 -0
@@ 111,6 111,7 @@ set(SOURCES
        queries/calendar/QueryEventsEditICS.cpp
        queries/calendar/QueryEventsEdit.cpp
        queries/calendar/QueryEventsGetFiltered.cpp
        queries/calendar/QueryEventsGetFilteredByDay.cpp
        queries/calendar/QueryEventsGetAllLimited.cpp
        queries/calendar/QueryEventsSelectFirstUpcoming.cpp
        queries/alarms/QueryAlarmsAdd.cpp

M module-db/Interface/AlarmsRecord.hpp => module-db/Interface/AlarmsRecord.hpp +1 -1
@@ 28,7 28,7 @@ namespace db::query::alarms

struct AlarmsRecord : public Record
{
    calendar::TimePoint time = TIME_POINT_INVALID;
    TimePoint time     = TIME_POINT_INVALID;
    uint32_t snooze    = 0;
    AlarmStatus status = AlarmStatus::On;
    uint32_t repeat    = 0;

M module-db/Interface/EventsRecord.cpp => module-db/Interface/EventsRecord.cpp +44 -8
@@ 10,6 10,7 @@
#include "module-db/queries/calendar/QueryEventsEdit.hpp"
#include <module-db/queries/calendar/QueryEventsEditICS.hpp>
#include <module-db/queries/calendar/QueryEventsGetFiltered.hpp>
#include <module-db/queries/calendar/QueryEventsGetFilteredByDay.hpp>
#include <module-db/queries/calendar/QueryEventsGetAllLimited.hpp>
#include <module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp>
#include <log/log.hpp>


@@ 70,8 71,8 @@ bool EventsRecordInterface::Add(const EventsRecord &rec)
    return false;
}

std::vector<EventsRecord> EventsRecordInterface::Select(calendar::TimePoint filter_from,
                                                        calendar::TimePoint filter_till,
std::vector<EventsRecord> EventsRecordInterface::Select(TimePoint filter_from,
                                                        TimePoint filter_till,
                                                        uint32_t offset,
                                                        uint32_t limit)
{


@@ 86,6 87,19 @@ std::vector<EventsRecord> EventsRecordInterface::Select(calendar::TimePoint filt
    return records;
}

std::vector<EventsRecord> EventsRecordInterface::Select(TimePoint date, uint32_t offset, uint32_t limit)
{
    auto rows = eventsDb->events.selectByDate(date, offset, limit);

    auto records = std::vector<EventsRecord>();

    for (auto &r : rows) {
        records.push_back(r);
    }

    return records;
}

std::unique_ptr<std::vector<EventsRecord>> EventsRecordInterface::GetLimitOffsetByField(uint32_t offset,
                                                                                        uint32_t limit,
                                                                                        EventsRecordField field,


@@ 265,13 279,17 @@ uint32_t EventsRecordInterface::GetCount()
    return eventsDb->events.count();
}

uint32_t EventsRecordInterface::GetCountFiltered(calendar::TimePoint from, calendar::TimePoint till)
uint32_t EventsRecordInterface::GetCountFiltered(TimePoint from, TimePoint till)
{
    return eventsDb->events.countFromFilter(from, till);
}

std::vector<EventsRecord> EventsRecordInterface::SelectFirstUpcoming(calendar::TimePoint filter_from,
                                                                     calendar::TimePoint filter_till)
uint32_t EventsRecordInterface::GetCountFilteredByDay(TimePoint filter)
{
    return eventsDb->events.countFromDayFilter(filter);
}

std::vector<EventsRecord> EventsRecordInterface::SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till)
{
    auto rows = eventsDb->events.SelectFirstUpcoming(filter_from, filter_till);



@@ 297,6 315,9 @@ std::unique_ptr<db::QueryResult> EventsRecordInterface::runQuery(std::shared_ptr
    if (typeid(*query) == typeid(db::query::events::GetFiltered)) {
        return runQueryImplGetFilteredResult(query);
    }
    if (typeid(*query) == typeid(db::query::events::GetFilteredByDay)) {
        return runQueryImplGetFilteredByDayResult(query);
    }
    if (typeid(*query) == typeid(db::query::events::Add)) {
        return runQueryImplAdd(query);
    }


@@ 354,12 375,27 @@ std::unique_ptr<db::query::events::GetFilteredResult> EventsRecordInterface::run
    std::shared_ptr<db::Query> query)
{
    auto getFilteredQuery = static_cast<db::query::events::GetFiltered *>(query.get());
    auto records          = Select(getFilteredQuery->filter_from,

    auto records = Select(getFilteredQuery->filter_from,
                          getFilteredQuery->filter_till,
                          getFilteredQuery->offset,
                          getFilteredQuery->limit);
    auto numberOfEvents   = GetCountFiltered(getFilteredQuery->filter_from, getFilteredQuery->filter_till);
    auto response         = std::make_unique<db::query::events::GetFilteredResult>(std::move(records), numberOfEvents);

    auto numberOfEvents = GetCountFiltered(getFilteredQuery->filter_from, getFilteredQuery->filter_till);
    auto response       = std::make_unique<db::query::events::GetFilteredResult>(std::move(records), numberOfEvents);
    response->setRequestQuery(query);
    return response;
}

std::unique_ptr<db::query::events::GetFilteredByDayResult> EventsRecordInterface::runQueryImplGetFilteredByDayResult(
    std::shared_ptr<db::Query> query)
{
    auto getFilteredQuery = static_cast<db::query::events::GetFilteredByDay *>(query.get());

    auto records = Select(getFilteredQuery->filterDate, getFilteredQuery->offset, getFilteredQuery->limit);

    auto numberOfEvents = GetCountFilteredByDay(getFilteredQuery->filterDate);
    auto response = std::make_unique<db::query::events::GetFilteredByDayResult>(std::move(records), numberOfEvents);
    response->setRequestQuery(query);
    return response;
}

M module-db/Interface/EventsRecord.hpp => module-db/Interface/EventsRecord.hpp +12 -9
@@ 23,6 23,8 @@ namespace db::query::events
    class GetAllLimitedResult;
    class GetFiltered;
    class GetFilteredResult;
    class GetFilteredByDay;
    class GetFilteredByDayResult;
    class Add;
    class AddResult;
    class Remove;


@@ 41,11 43,11 @@ struct EventsRecord : public Record
{
    std::string UID;
    std::string title;
    calendar::TimePoint date_from;
    calendar::TimePoint date_till;
    TimePoint date_from;
    TimePoint date_till;
    uint32_t reminder = 0;
    uint32_t repeat   = 0;
    calendar::TimePoint reminder_fired;
    TimePoint reminder_fired;
    std::string provider_type;
    std::string provider_id;
    std::string provider_iCalUid;


@@ 77,18 79,17 @@ class EventsRecordInterface : public RecordInterface<EventsRecord, EventsRecordF
    EventsRecord GetByID(uint32_t id) override final;
    EventsRecord GetByUID(const std::string &uid);
    uint32_t GetCount() override final;
    uint32_t GetCountFiltered(calendar::TimePoint from, calendar::TimePoint till);
    std::vector<EventsRecord> Select(calendar::TimePoint filter_from,
                                     calendar::TimePoint filter_till,
                                     uint32_t offset,
                                     uint32_t limit);
    uint32_t GetCountFiltered(TimePoint from, TimePoint till);
    uint32_t GetCountFilteredByDay(TimePoint filter);
    std::vector<EventsRecord> Select(TimePoint filter_from, TimePoint filter_till, uint32_t offset, uint32_t limit);
    std::vector<EventsRecord> Select(TimePoint date, uint32_t offset, uint32_t limit);
    std::unique_ptr<std::vector<EventsRecord>> GetLimitOffset(uint32_t offset, uint32_t limit) override final;
    std::unique_ptr<std::vector<EventsRecord>> GetLimitOffsetByField(uint32_t offset,
                                                                     uint32_t limit,
                                                                     EventsRecordField field,
                                                                     const char *str) override final;
    std::vector<EventsRecord> GetLimitOffsetByDate(uint32_t offset, uint32_t limit);
    std::vector<EventsRecord> SelectFirstUpcoming(calendar::TimePoint filter_from, calendar::TimePoint filter_till);
    std::vector<EventsRecord> SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till);

    std::unique_ptr<db::QueryResult> runQuery(std::shared_ptr<db::Query> query) override;



@@ 101,6 102,8 @@ class EventsRecordInterface : public RecordInterface<EventsRecord, EventsRecordF
        std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::GetFilteredResult> runQueryImplGetFilteredResult(
        std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::GetFilteredByDayResult> runQueryImplGetFilteredByDayResult(
        std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::AddResult> runQueryImplAdd(std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::RemoveResult> runQueryImplRemove(std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::RemoveICSResult> runQueryImplRemoveICS(std::shared_ptr<db::Query> query);

M module-db/Tables/AlarmsTable.cpp => module-db/Tables/AlarmsTable.cpp +1 -1
@@ 9,7 9,7 @@ AlarmsTableRow::AlarmsTableRow(const AlarmsRecord &rec)
{}

AlarmsTableRow::AlarmsTableRow(
    uint32_t id, calendar::TimePoint time, uint32_t snooze, AlarmStatus status, uint32_t repeat, UTF8 path)
    uint32_t id, TimePoint time, uint32_t snooze, AlarmStatus status, uint32_t repeat, UTF8 path)
    : Record{id}, time{time}, snooze{snooze}, status{status}, repeat{repeat}, path{std::move(path)}
{}


M module-db/Tables/AlarmsTable.hpp => module-db/Tables/AlarmsTable.hpp +2 -3
@@ 25,15 25,14 @@ enum class AlarmStatus

struct AlarmsTableRow : public Record
{
    calendar::TimePoint time = TIME_POINT_INVALID;
    TimePoint time     = TIME_POINT_INVALID;
    uint32_t snooze    = 0;
    AlarmStatus status = AlarmStatus::On;
    uint32_t repeat    = 0;
    UTF8 path;

    AlarmsTableRow() = default;
    AlarmsTableRow(
        uint32_t id, calendar::TimePoint time, uint32_t snooze, AlarmStatus status, uint32_t repeat, UTF8 path);
    AlarmsTableRow(uint32_t id, TimePoint time, uint32_t snooze, AlarmStatus status, uint32_t repeat, UTF8 path);
    explicit AlarmsTableRow(const AlarmsRecord &rec);
    explicit AlarmsTableRow(const QueryResult &result);
};

M module-db/Tables/EventsTable.cpp => module-db/Tables/EventsTable.cpp +70 -13
@@ 643,17 643,61 @@ EventsTableRow EventsTable::getByUID(const std::string &UID)
    };
}

std::vector<EventsTableRow> EventsTable::selectByDatePeriod(calendar::TimePoint date_filter,
                                                            calendar::TimePoint filter_till,
std::vector<EventsTableRow> EventsTable::selectByDatePeriod(TimePoint filterFrom,
                                                            TimePoint filterTill,
                                                            uint32_t offset,
                                                            uint32_t limit)
{
    auto retQuery = db->query("SELECT * FROM events WHERE date_from >= date('%q') AND date_from < date('%q', 'start of "
                              "day') ORDER BY datetime(date_from) LIMIT %u OFFSET %u;",
                              TimePointToString(date_filter).c_str(),
                              TimePointToString(filter_till).c_str(),
                              limit,
                              offset);
    auto retQuery = db->query(
        "SELECT * FROM events WHERE ( date('%q') >= date_from AND date_from > date('%q', 'start of day') ) OR "
        " ( date('%q') >= date_till AND date_till > date('%q', 'start of day') ) OR "
        " ( date_till > date('%q') AND date('%q', 'start of day') > date_from ) ORDER BY datetime(date_from) LIMIT %u "
        "OFFSET %u;",
        TimePointToString(filterFrom).c_str(),
        TimePointToString(filterTill).c_str(),
        TimePointToString(filterFrom).c_str(),
        TimePointToString(filterTill).c_str(),
        TimePointToString(filterFrom).c_str(),
        TimePointToString(filterTill).c_str(),
        limit,
        offset);

    if (retQuery == nullptr || retQuery->getRowCount() == 0) {
        return std::vector<EventsTableRow>();
    }

    std::vector<EventsTableRow> ret;

    do {
        ret.push_back(EventsTableRow{
            (*retQuery)[0].getUInt32(),                              // ID
            (*retQuery)[1].getString(),                              // UID
            (*retQuery)[2].getString(),                              // title
            TimePointFromString((*retQuery)[3].getString().c_str()), // date_from
            TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
            (*retQuery)[5].getUInt32(),                              // reminder
            (*retQuery)[6].getUInt32(),                              // repeat
            TimePointFromString((*retQuery)[7].getString().c_str()), // reminder_fired
            (*retQuery)[8].getString(),                              // provider type
            (*retQuery)[9].getString(),                              // provider id
            (*retQuery)[10].getString()                              // provider iCalUid
        });

    } while (retQuery->nextRow());

    return ret;
}

std::vector<EventsTableRow> EventsTable::selectByDate(TimePoint dateFilter, uint32_t offset, uint32_t limit)
{
    auto dateFilterFrom = dateFilter + date::days{1};
    auto retQuery       = db->query(
        "SELECT * FROM events WHERE date_from < date('%q', 'start of day') AND date('%q', 'start of day') <= date_till "
        "ORDER BY datetime(date_from) LIMIT %u OFFSET %u;",
        TimePointToString(dateFilterFrom).c_str(),
        TimePointToString(dateFilter).c_str(),
        limit,
        offset);

    if (retQuery == nullptr || retQuery->getRowCount() == 0) {
        return std::vector<EventsTableRow>();


@@ 714,7 758,6 @@ std::vector<EventsTableRow> EventsTable::getLimitOffset(uint32_t offset, uint32_

std::vector<EventsTableRow> EventsTable::getLimitOffsetByDate(uint32_t offset, uint32_t limit)
{

    auto retQuery = db->query("SELECT * from events ORDER BY datetime(date_from) LIMIT %u OFFSET %u;", limit, offset);

    if (retQuery == nullptr || retQuery->getRowCount() == 0) {


@@ 770,10 813,10 @@ uint32_t EventsTable::count()
    return (*queryRet)[0].getUInt32();
}

uint32_t EventsTable::countFromFilter(calendar::TimePoint from, calendar::TimePoint till)
uint32_t EventsTable::countFromFilter(TimePoint from, TimePoint till)
{
    auto queryRet = db->query(
        "SELECT COUNT(*) FROM events WHERE date_from >= date('%q') AND date_from < date('%q', 'start of day');",
        "SELECT COUNT(*) FROM events WHERE date('%q') <= date_from AND date_from < date('%q', 'start of day');",
        TimePointToString(from).c_str(),
        TimePointToString(till).c_str());



@@ 784,6 827,21 @@ uint32_t EventsTable::countFromFilter(calendar::TimePoint from, calendar::TimePo
    return (*queryRet)[0].getUInt32();
}

uint32_t EventsTable::countFromDayFilter(TimePoint filter)
{
    auto filterFrom = filter + date::days{1};
    auto queryRet   = db->query("SELECT COUNT(*) FROM events WHERE date_from < datetime('%q', 'start of day') AND "
                              "datetime('%q', 'start of day') <= date_till;",
                              TimePointToString(filterFrom).c_str(),
                              TimePointToString(filter).c_str());

    if (queryRet == nullptr || queryRet->getRowCount() == 0) {
        return 0;
    }

    return (*queryRet)[0].getUInt32();
}

uint32_t EventsTable::countByFieldId(const char *field, uint32_t id)
{
    assert(0 && "Not implemented");


@@ 791,8 849,7 @@ uint32_t EventsTable::countByFieldId(const char *field, uint32_t id)
    return 0;
}

std::vector<EventsTableRow> EventsTable::SelectFirstUpcoming(calendar::TimePoint filter_from,
                                                             calendar::TimePoint filter_till)
std::vector<EventsTableRow> EventsTable::SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till)
{
    auto retQuery = db->query("SELECT DATETIME(date_from, '-' || reminder || ' minutes') AS calc_dt, * "
                              "FROM events "

M module-db/Tables/EventsTable.hpp => module-db/Tables/EventsTable.hpp +11 -7
@@ 14,11 14,11 @@ struct EventsTableRow : public Record
{
    std::string UID;
    std::string title;
    calendar::TimePoint date_from      = TIME_POINT_INVALID;
    calendar::TimePoint date_till      = TIME_POINT_INVALID;
    TimePoint date_from      = TIME_POINT_INVALID;
    TimePoint date_till      = TIME_POINT_INVALID;
    uint32_t reminder        = 0;
    uint32_t repeat          = 0;
    calendar::TimePoint reminder_fired = TIME_POINT_INVALID;
    TimePoint reminder_fired = TIME_POINT_INVALID;
    std::string provider_type;
    std::string provider_id;
    std::string provider_iCalUid;


@@ 53,12 53,16 @@ class EventsTable : public Table<EventsTableRow, EventsTableFields>
    bool drop();
    EventsTableRow getByUID(const std::string &UID);
    EventsTableRow getById(uint32_t id) override final;
    std::vector<EventsTableRow> selectByDatePeriod(calendar::TimePoint filter_from,
                                                   calendar::TimePoint filter_till,
    std::vector<EventsTableRow> selectByDatePeriod(TimePoint filter_from,
                                                   TimePoint filter_till,
                                                   uint32_t offset,
                                                   uint32_t limit);

    std::vector<EventsTableRow> selectByDate(TimePoint date, uint32_t offset, uint32_t limit);

    uint32_t count() override final;
    uint32_t countFromFilter(calendar::TimePoint from, calendar::TimePoint till);
    uint32_t countFromFilter(TimePoint from, TimePoint till);
    uint32_t countFromDayFilter(TimePoint filter);
    uint32_t countByFieldId(const char *field, uint32_t id) override final;
    std::vector<EventsTableRow> getLimitOffset(uint32_t offset, uint32_t limit) override final;
    std::vector<EventsTableRow> getLimitOffsetByField(uint32_t offset,


@@ 67,7 71,7 @@ class EventsTable : public Table<EventsTableRow, EventsTableFields>
                                                      const char *str) override final;

    std::vector<EventsTableRow> getLimitOffsetByDate(uint32_t offset, uint32_t limit);
    std::vector<EventsTableRow> SelectFirstUpcoming(calendar::TimePoint filter_from, calendar::TimePoint filter_till);
    std::vector<EventsTableRow> SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till);

  private:
    const char *createTableQuery = "CREATE TABLE IF NOT EXISTS events("

M module-db/queries/calendar/QueryEventsGetFiltered.cpp => module-db/queries/calendar/QueryEventsGetFiltered.cpp +1 -4
@@ 5,10 5,7 @@

namespace db::query::events
{
    GetFiltered::GetFiltered(calendar::TimePoint filter_from,
                             calendar::TimePoint filter_till,
                             uint32_t offset,
                             uint32_t limit)
    GetFiltered::GetFiltered(TimePoint filter_from, TimePoint filter_till, uint32_t offset, uint32_t limit)
        : Query(Query::Type::Read), filter_from(filter_from), filter_till(filter_till), offset(offset), limit(limit)
    {}


M module-db/queries/calendar/QueryEventsGetFiltered.hpp => module-db/queries/calendar/QueryEventsGetFiltered.hpp +3 -6
@@ 13,14 13,11 @@ namespace db::query::events
    class GetFiltered : public Query
    {
      public:
        GetFiltered(calendar::TimePoint filter_from,
                    calendar::TimePoint filter_till,
                    uint32_t offset = 0,
                    uint32_t limit  = UINT32_MAX);
        GetFiltered(TimePoint filter_from, TimePoint filter_till, uint32_t offset = 0, uint32_t limit = UINT32_MAX);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        calendar::TimePoint filter_from;
        calendar::TimePoint filter_till;
        TimePoint filter_from;
        TimePoint filter_till;
        uint32_t offset;
        uint32_t limit;
    };

A module-db/queries/calendar/QueryEventsGetFilteredByDay.cpp => module-db/queries/calendar/QueryEventsGetFilteredByDay.cpp +35 -0
@@ 0,0 1,35 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryEventsGetFilteredByDay.hpp"

namespace db::query::events
{
    GetFilteredByDay::GetFilteredByDay(TimePoint filterDate, uint32_t offset, uint32_t limit)
        : Query(Query::Type::Read), filterDate(filterDate), offset(offset), limit(limit)
    {}

    auto GetFilteredByDay::debugInfo() const -> std::string
    {
        return "GetFilteredByDay";
    }

    GetFilteredByDayResult::GetFilteredByDayResult(const std::vector<EventsRecord> &records, uint32_t count)
        : records{std::move(records)}, recordsCount{count}
    {}

    auto GetFilteredByDayResult::getResult() -> std::vector<EventsRecord>
    {
        return records;
    }

    auto GetFilteredByDayResult::getCountResult() -> uint32_t
    {
        return recordsCount;
    }

    auto GetFilteredByDayResult::debugInfo() const -> std::string
    {
        return "GetFilteredByDayResult";
    }
} // namespace db::query::events

A module-db/queries/calendar/QueryEventsGetFilteredByDay.hpp => module-db/queries/calendar/QueryEventsGetFilteredByDay.hpp +37 -0
@@ 0,0 1,37 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Interface/EventsRecord.hpp"
#include <Common/Query.hpp>
#include <string>
#include <module-apps/application-calendar/data/dateCommon.hpp>

namespace db::query::events
{
    class GetFilteredByDay : public Query
    {
      public:
        GetFilteredByDay(TimePoint filterDate, uint32_t offset = 0, uint32_t limit = UINT32_MAX);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        TimePoint filterDate;
        uint32_t offset;
        uint32_t limit;
    };

    class GetFilteredByDayResult : public QueryResult
    {
        std::vector<EventsRecord> records;
        uint32_t recordsCount;

      public:
        GetFilteredByDayResult(const std::vector<EventsRecord> &records, uint32_t count);
        auto getCountResult() -> uint32_t;
        [[nodiscard]] auto getResult() -> std::vector<EventsRecord>;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

} // namespace db::query::events

M module-db/queries/calendar/QueryEventsSelectFirstUpcoming.cpp => module-db/queries/calendar/QueryEventsSelectFirstUpcoming.cpp +1 -1
@@ 5,7 5,7 @@

namespace db::query::events
{
    SelectFirstUpcoming::SelectFirstUpcoming(calendar::TimePoint filter_from, calendar::TimePoint filter_till)
    SelectFirstUpcoming::SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till)
        : Query(Query::Type::Read), filter_from(filter_from), filter_till(filter_till)
    {}


M module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp => module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp +3 -3
@@ 14,11 14,11 @@ namespace db::query::events
    class SelectFirstUpcoming : public Query
    {
      public:
        SelectFirstUpcoming(calendar::TimePoint filter_from, calendar::TimePoint filter_till);
        SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        calendar::TimePoint filter_from;
        calendar::TimePoint filter_till;
        TimePoint filter_from;
        TimePoint filter_till;
    };

    /// Result of SelectFirstUpcoming query

M module-db/tests/AlarmsRecord_tests.cpp => module-db/tests/AlarmsRecord_tests.cpp +1 -1
@@ 184,7 184,7 @@ TEST_CASE("Alarms Record tests")
    }

    auto getQuery = [&](uint32_t id,
                        calendar::TimePoint alarmTime,
                        TimePoint alarmTime,
                        uint32_t snooze,
                        AlarmStatus status,
                        uint32_t repeat,

M module-db/tests/AlarmsTable_tests.cpp => module-db/tests/AlarmsTable_tests.cpp +5 -5
@@ 139,11 139,11 @@ TEST_CASE("Alarms Table tests")
        REQUIRE(alarmsTbl.add(
            AlarmsTableRow(5, TimePointFromString("2020-12-11 07:15:00"), 1, AlarmStatus::On, 1, "file2.mp3")));

        const std::array<calendar::TimePoint, 5> paramTime{TimePointFromString("2020-12-11 07:15:00"),
                                                           TimePointFromString("2020-11-11 15:10:00"),
                                                           TimePointFromString("2020-11-11 15:15:00"),
                                                           TimePointFromString("2020-11-12 17:10:00"),
                                                           TimePointFromString("2020-11-11 19:25:00")};
        const std::array<TimePoint, 5> paramTime{TimePointFromString("2020-12-11 07:15:00"),
                                                 TimePointFromString("2020-11-11 15:10:00"),
                                                 TimePointFromString("2020-11-11 15:15:00"),
                                                 TimePointFromString("2020-11-12 17:10:00"),
                                                 TimePointFromString("2020-11-11 19:25:00")};

        REQUIRE(alarmsTbl.count() == 5);
        auto entries   = alarmsTbl.getLimitOffset(0, 5);

M module-db/tests/EventsRecord_tests.cpp => module-db/tests/EventsRecord_tests.cpp +3 -6
@@ 730,7 730,7 @@ TEST_CASE("Events Record tests")

    SECTION("Select first upcoming event")
    {
        calendar::TimePoint start_date = TimePointFromString("2019-10-19 14:24:00");
        TimePoint start_date = TimePointFromString("2019-10-19 14:24:00");
        auto nextUpcoming    = eventsRecordInterface.SelectFirstUpcoming(start_date, start_date);
        REQUIRE(nextUpcoming.size() == 1);
        EventsRecord nextEventsRecord = nextUpcoming.at(0);


@@ 799,9 799,7 @@ TEST_CASE("Events Record tests")
        getAll(expectedRecords, numberOfEvents);
    }

    auto getFiltered = [&](calendar::TimePoint filter_from,
                           calendar::TimePoint filter_till,
                           std::vector<EventsRecord> expected_records) {
    auto getFiltered = [&](TimePoint filter_from, TimePoint filter_till, std::vector<EventsRecord> expected_records) {
        auto query  = std::make_shared<db::query::events::GetFiltered>(filter_from, filter_till);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::GetFilteredResult *>(ret.get());


@@ 1013,8 1011,7 @@ TEST_CASE("Events Record tests")
        EditQueryICS(testRecord6.UID, testRecord6);
    }

    [[maybe_unused]] auto selectFirstUpcomingEvent = [&](calendar::TimePoint filter_from,
                                                         calendar::TimePoint filter_till) {
    [[maybe_unused]] auto selectFirstUpcomingEvent = [&](TimePoint filter_from, TimePoint filter_till) {
        auto query  = std::make_shared<db::query::events::SelectFirstUpcoming>(filter_from, filter_till);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::SelectFirstUpcomingResult *>(ret.get());

M module-db/tests/EventsTable_tests.cpp => module-db/tests/EventsTable_tests.cpp +324 -39
@@ 34,7 34,7 @@ TEST_CASE("Events Table tests")
    Database::initialize();

    const auto eventsPath = (purefs::dir::getUserDiskPath() / "events.db").c_str();
    std::filesystem::remove(eventsPath);
    // std::filesystem::remove(eventsPath);

    EventsDB eventsDb{eventsPath};
    REQUIRE(eventsDb.isInitialized());


@@ 166,8 166,8 @@ TEST_CASE("Events Table tests")
        CHECK(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 7;
        calendar::TimePoint startDate = TimePointFromString("2019-10-20 14:30:00");
        calendar::TimePoint endDate   = TimePointFromString("2019-10-20 15:30:00");
        TimePoint startDate     = TimePointFromString("2019-10-20 14:30:00");
        TimePoint endDate       = TimePointFromString("2019-10-20 15:30:00");
        testRow1.date_from      = startDate;
        testRow1.date_till      = endDate;
        CHECK(eventsTbl.addDaily(testRow1));


@@ 201,8 201,8 @@ TEST_CASE("Events Table tests")
        CHECK(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 4;
        calendar::TimePoint startDate = TimePointFromString("2019-10-20 14:30:00");
        calendar::TimePoint endDate   = TimePointFromString("2019-10-20 15:30:00");
        TimePoint startDate     = TimePointFromString("2019-10-20 14:30:00");
        TimePoint endDate       = TimePointFromString("2019-10-20 15:30:00");
        testRow1.date_from      = startDate;
        testRow1.date_till      = endDate;
        CHECK(eventsTbl.addWeekly(testRow1));


@@ 236,8 236,8 @@ TEST_CASE("Events Table tests")
        CHECK(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 4;
        calendar::TimePoint startDate = TimePointFromString("2019-10-20 14:30:00");
        calendar::TimePoint endDate   = TimePointFromString("2019-10-20 15:30:00");
        TimePoint startDate     = TimePointFromString("2019-10-20 14:30:00");
        TimePoint endDate       = TimePointFromString("2019-10-20 15:30:00");
        testRow1.date_from      = startDate;
        testRow1.date_till      = endDate;
        CHECK(eventsTbl.addTwoWeeks(testRow1));


@@ 271,7 271,7 @@ TEST_CASE("Events Table tests")
        CHECK(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 12;
        const std::array<calendar::TimePoint, 24> dates{
        const std::array<TimePoint, 24> dates{
            TimePointFromString("2019-01-20 14:30:00"), TimePointFromString("2019-01-20 15:30:00"),
            TimePointFromString("2019-02-20 14:30:00"), TimePointFromString("2019-02-20 15:30:00"),
            TimePointFromString("2019-03-20 14:30:00"), TimePointFromString("2019-03-20 15:30:00"),


@@ 318,14 318,14 @@ TEST_CASE("Events Table tests")
        REQUIRE(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 4;
        std::array<calendar::TimePoint, 8> dates{TimePointFromString("2019-02-20 14:30:00"),
                                                 TimePointFromString("2019-02-20 15:30:00"),
                                                 TimePointFromString("2020-02-20 14:30:00"),
                                                 TimePointFromString("2020-02-20 15:30:00"),
                                                 TimePointFromString("2021-02-20 14:30:00"),
                                                 TimePointFromString("2021-02-20 15:30:00"),
                                                 TimePointFromString("2022-02-20 14:30:00"),
                                                 TimePointFromString("2022-02-20 15:30:00")};
        std::array<TimePoint, 8> dates{TimePointFromString("2019-02-20 14:30:00"),
                                       TimePointFromString("2019-02-20 15:30:00"),
                                       TimePointFromString("2020-02-20 14:30:00"),
                                       TimePointFromString("2020-02-20 15:30:00"),
                                       TimePointFromString("2021-02-20 14:30:00"),
                                       TimePointFromString("2021-02-20 15:30:00"),
                                       TimePointFromString("2022-02-20 14:30:00"),
                                       TimePointFromString("2022-02-20 15:30:00")};

        testRow1.date_from = dates[0];
        testRow1.date_till = dates[1];


@@ 367,8 367,8 @@ TEST_CASE("Events Table tests")
    {
        auto check_custom_repeat = [&](uint32_t customRepeatOption,
                                       uint32_t numberOfEvents,
                                       calendar::TimePoint originalStartDate,
                                       calendar::TimePoint originalEndDate) {
                                       TimePoint originalStartDate,
                                       TimePoint originalEndDate) {
            if (eventsTbl.count() > 0) {
                REQUIRE(remove_events(eventsDb));
            }


@@ 396,8 396,8 @@ TEST_CASE("Events Table tests")
                }
            }

            calendar::TimePoint expectedStartDate = TimePointFromString("2020-12-07 14:30:00"); // monday
            calendar::TimePoint expectedEndDate   = TimePointFromString("2020-12-07 15:30:00"); // monday
            TimePoint expectedStartDate = TimePointFromString("2020-12-07 14:30:00"); // monday
            TimePoint expectedEndDate   = TimePointFromString("2020-12-07 15:30:00"); // monday

            uint32_t i = 0;
            for (uint32_t l = 0; l < numberOfWeeks; l++) {


@@ 439,8 439,8 @@ TEST_CASE("Events Table tests")
            uint32_t customRepeatOption =
                static_cast<uint32_t>(weekDayOption::monday) + static_cast<uint32_t>(weekDayOption::wednesday);
            uint32_t numberOfEvents     = 9;
            calendar::TimePoint originalStartDate = TimePointFromString("2020-12-10 14:30:00"); // thursday
            calendar::TimePoint originalEndDate   = TimePointFromString("2020-12-10 15:30:00"); // thursday
            TimePoint originalStartDate = TimePointFromString("2020-12-10 14:30:00"); // thursday
            TimePoint originalEndDate   = TimePointFromString("2020-12-10 15:30:00"); // thursday

            check_custom_repeat(customRepeatOption, numberOfEvents, originalStartDate, originalEndDate);
        }


@@ 451,8 451,8 @@ TEST_CASE("Events Table tests")
                static_cast<uint32_t>(weekDayOption::monday) + static_cast<uint32_t>(weekDayOption::wednesday) +
                static_cast<uint32_t>(weekDayOption::tuesday) + static_cast<uint32_t>(weekDayOption::sunday);
            uint32_t numberOfEvents     = 17;
            calendar::TimePoint originalStartDate = TimePointFromString("2020-12-10 14:30:00"); // thursday
            calendar::TimePoint originalEndDate   = TimePointFromString("2020-12-10 15:30:00"); // thursday
            TimePoint originalStartDate = TimePointFromString("2020-12-10 14:30:00"); // thursday
            TimePoint originalEndDate   = TimePointFromString("2020-12-10 15:30:00"); // thursday

            check_custom_repeat(customRepeatOption, numberOfEvents, originalStartDate, originalEndDate);
        }


@@ 461,8 461,8 @@ TEST_CASE("Events Table tests")
        {
            uint32_t customRepeatOption = static_cast<uint32_t>(weekDayOption::saturday);
            uint32_t numberOfEvents     = 5;
            calendar::TimePoint originalStartDate = TimePointFromString("2020-12-10 14:30:00"); // thursday
            calendar::TimePoint originalEndDate   = TimePointFromString("2020-12-10 15:30:00"); // thursday
            TimePoint originalStartDate = TimePointFromString("2020-12-10 14:30:00"); // thursday
            TimePoint originalEndDate   = TimePointFromString("2020-12-10 15:30:00"); // thursday

            check_custom_repeat(customRepeatOption, numberOfEvents, originalStartDate, originalEndDate);
        }


@@ 470,8 470,8 @@ TEST_CASE("Events Table tests")

    SECTION("Check count from filter")
    {
        calendar::TimePoint from = TimePointFromString("2019-10-20 14:30:00");
        calendar::TimePoint till = TimePointFromString("2019-10-24 14:20:00");
        TimePoint from = TimePointFromString("2019-10-20 14:30:00");
        TimePoint till = TimePointFromString("2019-10-24 14:20:00");

        CHECK(eventsTbl.countFromFilter(from, till) == 4);
    }


@@ 516,8 516,8 @@ TEST_CASE("Events Table tests")
        CHECK(eventsTbl.count() == 6);

        std::string newTitle = "Updated Title", newProviderID = "PurePhoneUpdated";
        calendar::TimePoint newDateFrom = TimePointFromString("2020-10-20 15:00:00"),
                            newDateTill = TimePointFromString("2020-10-20 16:00:00");
        TimePoint newDateFrom    = TimePointFromString("2020-10-20 15:00:00"),
                  newDateTill    = TimePointFromString("2020-10-20 16:00:00");
        uint32_t newReminder     = static_cast<uint32_t>(Reminder::one_week_before);
        uint32_t newRepeatOption = static_cast<uint32_t>(Repeat::biweekly);



@@ 572,8 572,8 @@ TEST_CASE("Events Table tests")

        std::string newTitle = "Updated Title", newProviderType = "PurePhoneUpdate", newProviderID = "newID",
                    newProvideriCalUid = "new iCalUid";
        calendar::TimePoint newDateFrom = TimePointFromString("2020-10-20 15:00:00"),
                            newDateTill = TimePointFromString("2020-10-20 16:00:00");
        TimePoint newDateFrom          = TimePointFromString("2020-10-20 15:00:00"),
                  newDateTill          = TimePointFromString("2020-10-20 16:00:00");
        uint32_t newReminder           = static_cast<uint32_t>(Reminder::one_week_before);
        uint32_t newRepeatOption       = static_cast<uint32_t>(Repeat::biweekly);



@@ 630,8 630,8 @@ TEST_CASE("Events Table tests")

        std::string newTitle = "Updated Title", newProviderType = "PurePhoneUpdate", newProviderID = "newID",
                    newProvideriCalUid = "new iCalUid";
        calendar::TimePoint newDateFrom = TimePointFromString("2020-10-20 15:00:00"),
                            newDateTill = TimePointFromString("2020-10-20 16:00:00");
        TimePoint newDateFrom          = TimePointFromString("2020-10-20 15:00:00"),
                  newDateTill          = TimePointFromString("2020-10-20 16:00:00");
        uint32_t newReminder           = static_cast<uint32_t>(Reminder::one_week_before);
        uint32_t newRepeatOption       = static_cast<uint32_t>(Repeat::biweekly);



@@ 696,7 696,7 @@ TEST_CASE("Events Table tests")
        check_tableRow(entries[2], testRow3);
    }

    SECTION("Select entry by date max")
    SECTION("Select entry by date period max")
    {
        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("2019-10-21 10:00:00"), TimePointFromString("2019-10-24 10:00:00"), 0, UINT32_MAX);


@@ 722,6 722,291 @@ TEST_CASE("Events Table tests")
        CHECK(entries.size() == 6);
    }

    SECTION("Check multiday events")
    {
        /// 8.12 <-> 11.12
        EventsTableRow multidayEvent1 = {{1},
                                         .UID              = "test1",
                                         .title            = "Event1",
                                         .date_from        = TimePointFromString("2020-12-8 14:25:00"),
                                         .date_till        = TimePointFromString("2020-12-11 15:36:00"),
                                         .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                                         .repeat           = static_cast<uint32_t>(Repeat::never),
                                         .reminder_fired   = TimePointFromString("2020-10-20 14:20:00"),
                                         .provider_type    = "PurePhone",
                                         .provider_id      = "testID",
                                         .provider_iCalUid = "test6"};

        /// 14.12 <-> 21.12
        EventsTableRow multidayEvent2 = {{2},
                                         .UID              = "test2",
                                         .title            = "Event2",
                                         .date_from        = TimePointFromString("2020-12-14 14:24:00"),
                                         .date_till        = TimePointFromString("2020-12-21 15:36:00"),
                                         .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                                         .repeat           = static_cast<uint32_t>(Repeat::never),
                                         .reminder_fired   = TimePointFromString("2020-10-21 14:20:00"),
                                         .provider_type    = "PurePhone",
                                         .provider_id      = "testID",
                                         .provider_iCalUid = "test6"};

        EventsTableRow singleDayEvent = {{3},
                                         .UID              = "test3",
                                         .title            = "Event3",
                                         .date_from        = TimePointFromString("2020-12-18 14:24:00"),
                                         .date_till        = TimePointFromString("2020-12-18 15:36:00"),
                                         .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                                         .repeat           = static_cast<uint32_t>(Repeat::never),
                                         .reminder_fired   = TimePointFromString("2020-10-21 14:20:00"),
                                         .provider_type    = "PurePhone",
                                         .provider_id      = "testID",
                                         .provider_iCalUid = "test6"};

        /// 23.12 <-> 24.12
        EventsTableRow multidayEvent3 = {{4},
                                         .UID              = "test4",
                                         .title            = "Event4",
                                         .date_from        = TimePointFromString("2020-12-23 14:25:00"),
                                         .date_till        = TimePointFromString("2020-12-24 15:36:00"),
                                         .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                                         .repeat           = static_cast<uint32_t>(Repeat::never),
                                         .reminder_fired   = TimePointFromString("2020-10-22 14:20:00"),
                                         .provider_type    = "PurePhone",
                                         .provider_id      = "testID",
                                         .provider_iCalUid = "test6"};

        /// 24.12 <-> 26.12
        EventsTableRow multidayEvent4 = {{5},
                                         .UID              = "test5",
                                         .title            = "Event5",
                                         .date_from        = TimePointFromString("2020-12-24 16:25:00"),
                                         .date_till        = TimePointFromString("2020-12-26 15:36:00"),
                                         .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                                         .repeat           = static_cast<uint32_t>(Repeat::never),
                                         .reminder_fired   = TimePointFromString("2020-10-22 14:20:00"),
                                         .provider_type    = "PurePhone",
                                         .provider_id      = "testID",
                                         .provider_iCalUid = "test6"};

        /// 25.12 <-> 28.12
        EventsTableRow multidayEvent5 = {{6},
                                         .UID              = "test6",
                                         .title            = "Event6",
                                         .date_from        = TimePointFromString("2020-12-25 16:25:00"),
                                         .date_till        = TimePointFromString("2020-12-28 15:36:00"),
                                         .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                                         .repeat           = static_cast<uint32_t>(Repeat::never),
                                         .reminder_fired   = TimePointFromString("2020-10-22 14:20:00"),
                                         .provider_type    = "PurePhone",
                                         .provider_id      = "testID",
                                         .provider_iCalUid = "test6"};

        /// 31.12 <-> 03.01
        EventsTableRow multidayEvent6 = {{7},
                                         .UID              = "test7",
                                         .title            = "Event7",
                                         .date_from        = TimePointFromString("2020-12-31 16:25:00"),
                                         .date_till        = TimePointFromString("2021-01-23 15:36:00"),
                                         .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                                         .repeat           = static_cast<uint32_t>(Repeat::never),
                                         .reminder_fired   = TimePointFromString("2020-10-22 14:20:00"),
                                         .provider_type    = "PurePhone",
                                         .provider_id      = "testID",
                                         .provider_iCalUid = "test6"};

        SECTION("Select entry by date (multi day events)")
        {
            auto entries = eventsTbl.selectByDate(TimePointFromString("2020-12-07 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.empty());

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-08 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent1);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-09 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent1);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-10 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent1);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-11 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent1);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-12 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.empty());

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-13 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.empty());

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-14 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent2);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-15 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent2);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-16 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent2);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-17 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent2);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-18 10:00:00"), 0, UINT32_MAX);
            REQUIRE(entries.size() == 2);

            check_tableRow(entries[0], multidayEvent2);
            check_tableRow(entries[1], singleDayEvent);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-19 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent2);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-20 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent2);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-21 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent2);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-22 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.empty());

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-23 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent3);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-24 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 2);

            check_tableRow(entries[0], multidayEvent3);
            check_tableRow(entries[1], multidayEvent4);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-25 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 2);

            check_tableRow(entries[0], multidayEvent4);
            check_tableRow(entries[1], multidayEvent5);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-26 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 2);

            check_tableRow(entries[0], multidayEvent4);
            check_tableRow(entries[1], multidayEvent5);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-27 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent5);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-28 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent5);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-29 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.empty());

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-30 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.empty());

            entries = eventsTbl.selectByDate(TimePointFromString("2020-12-31 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent6);

            entries = eventsTbl.selectByDate(TimePointFromString("2021-01-01 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent6);

            entries = eventsTbl.selectByDate(TimePointFromString("2021-01-02 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent6);

            entries = eventsTbl.selectByDate(TimePointFromString("2021-01-03 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], multidayEvent6);

            entries = eventsTbl.selectByDate(TimePointFromString("2021-01-04 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.empty());
        }

        SECTION("Select entry by date (single day events)")
        {
            EventsTableRow singleDayEvent1 = {{1},
                                              .UID              = "test1",
                                              .title            = "Event1",
                                              .date_from        = TimePointFromString("2020-10-20 14:25:00"),
                                              .date_till        = TimePointFromString("2020-10-20 15:36:00"),
                                              .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                                              .repeat           = static_cast<uint32_t>(Repeat::never),
                                              .reminder_fired   = TimePointFromString("2020-10-20 14:20:00"),
                                              .provider_type    = "PurePhone",
                                              .provider_id      = "testID",
                                              .provider_iCalUid = "test6"};

            EventsTableRow singleDayEvent2 = {{2},
                                              .UID              = "test2",
                                              .title            = "Event2",
                                              .date_from        = TimePointFromString("2020-10-21 14:24:00"),
                                              .date_till        = TimePointFromString("2020-10-21 15:36:00"),
                                              .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                                              .repeat           = static_cast<uint32_t>(Repeat::never),
                                              .reminder_fired   = TimePointFromString("2020-10-21 14:20:00"),
                                              .provider_type    = "PurePhone",
                                              .provider_id      = "testID",
                                              .provider_iCalUid = "test6"};

            EventsTableRow singleDayEvent3 = {{3},
                                              .UID              = "test3",
                                              .title            = "Event3",
                                              .date_from        = TimePointFromString("2020-10-22 14:25:00"),
                                              .date_till        = TimePointFromString("2020-10-22 15:36:00"),
                                              .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                                              .repeat           = static_cast<uint32_t>(Repeat::never),
                                              .reminder_fired   = TimePointFromString("2020-10-22 14:20:00"),
                                              .provider_type    = "PurePhone",
                                              .provider_id      = "testID",
                                              .provider_iCalUid = "test6"};

            auto entries = eventsTbl.selectByDate(TimePointFromString("2020-10-20 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], singleDayEvent1);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-10-21 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], singleDayEvent2);

            entries = eventsTbl.selectByDate(TimePointFromString("2020-10-22 10:00:00"), 0, UINT32_MAX);
            CHECK(entries.size() == 1);

            check_tableRow(entries[0], singleDayEvent3);
        }
    }

    SECTION("Select first upcoming event")
    {
        if (eventsTbl.count() > 0) {


@@ 729,11 1014,11 @@ TEST_CASE("Events Table tests")
        }
        CHECK(eventsTbl.count() == 0);

        calendar::TimePoint startDate1 = TimePointFromString("2018-10-20 14:24:00");
        calendar::TimePoint startDate2 = TimePointFromString("2020-10-20 14:24:00");
        TimePoint startDate1 = TimePointFromString("2018-10-20 14:24:00");
        TimePoint startDate2 = TimePointFromString("2020-10-20 14:24:00");

        calendar::TimePoint tillDate  = TimePointFromString("2030-10-20 15:24:00");
        calendar::TimePoint firedDate = TimePointFromString("2018-10-20 14:24:00");
        TimePoint tillDate  = TimePointFromString("2030-10-20 15:24:00");
        TimePoint firedDate = TimePointFromString("2018-10-20 14:24:00");

        EventsTableRow testEvent1 = {{1},
                                     .UID              = "test1",

M module-services/service-time/service-time/CalendarTimeEvents.hpp => module-services/service-time/service-time/CalendarTimeEvents.hpp +1 -1
@@ 27,7 27,7 @@ namespace stm
    {
      private:
        EventsRecord eventRecord;
        calendar::TimePoint startTP = TIME_POINT_INVALID;
        TimePoint startTP = TIME_POINT_INVALID;

      protected:
        const std::string timerName() override

M module-services/service-time/timeEvents/CalendarTimeEvents.cpp => module-services/service-time/timeEvents/CalendarTimeEvents.cpp +2 -2
@@ 37,8 37,8 @@ namespace stm

    bool CalendarTimeEvents::sendNextEventQuery()
    {
        calendar::TimePoint filterFrom = TimePointNow();
        calendar::TimePoint filterTill = filterFrom;
        TimePoint filterFrom = TimePointNow();
        TimePoint filterTill = filterFrom;
        if (startTP != TIME_POINT_INVALID) {
            filterFrom = std::min(startTP, filterFrom);
            filterTill = filterFrom;

M module-utils/ical/ParserICS.cpp => module-utils/ical/ParserICS.cpp +5 -8
@@ 446,7 446,7 @@ auto Event::timeStringFrom(const std::string &icalTime) const -> std::string
           getSecondsFromIcalTime(icalTime);
}

auto Event::TimePointFromIcalDate(const std::string &icalDateTime) const -> calendar::TimePoint
auto Event::TimePointFromIcalDate(const std::string &icalDateTime) const -> TimePoint
{
    std::string icalDate(getDateFromIcalFormat(icalDateTime));
    std::string icalTime(getTimeFromIcalFormat(icalDateTime));


@@ 459,7 459,7 @@ auto Event::TimePointFromIcalDate(const std::string &icalDateTime) const -> cale
    return TimePointFromString(dateTime.c_str());
}

auto Event::TimePointToIcalDate(const calendar::TimePoint &tp) const -> std::string
auto Event::TimePointToIcalDate(const TimePoint &tp) const -> std::string
{
    constexpr uint32_t bufferLimit = 16;
    auto time                      = TimePointToTimeT(tp);


@@ 541,10 541,7 @@ auto Event::validateUID(const std::string &UID) -> bool
    return validateDT(DTimestamp);
}

Event::Event(const std::string &summary,
             const calendar::TimePoint from,
             calendar::TimePoint till,
             const std::string &uid)
Event::Event(const std::string &summary, const TimePoint from, TimePoint till, const std::string &uid)
{
    if (summary.empty()) {
        isValid = false;


@@ 598,12 595,12 @@ auto Event::getSummary() const -> std::string
    return summary;
}

auto Event::getDTStartTimePoint() const -> calendar::TimePoint
auto Event::getDTStartTimePoint() const -> TimePoint
{
    return dtstart;
}

auto Event::getDTEndTimePoint() const -> calendar::TimePoint
auto Event::getDTEndTimePoint() const -> TimePoint
{
    return dtend;
}

M module-utils/ical/ParserICS.hpp => module-utils/ical/ParserICS.hpp +7 -7
@@ 91,8 91,8 @@ class Event
{
    std::string uid;
    std::string summary;
    calendar::TimePoint dtstart;
    calendar::TimePoint dtend;
    TimePoint dtstart;
    TimePoint dtend;

    auto isDate(const std::string &dt) -> bool;
    auto isTime(const std::string &dt) -> bool;


@@ 112,13 112,13 @@ class Event
    [[nodiscard]] auto dateStringFrom(const std::string &icalDate) const -> std::string;
    [[nodiscard]] auto timeStringFrom(const std::string &icalTime) const -> std::string;

    [[nodiscard]] auto TimePointFromIcalDate(const std::string &icalDateTime) const -> calendar::TimePoint;
    [[nodiscard]] auto TimePointToIcalDate(const calendar::TimePoint &tp) const -> std::string;
    [[nodiscard]] auto TimePointFromIcalDate(const std::string &icalDateTime) const -> TimePoint;
    [[nodiscard]] auto TimePointToIcalDate(const TimePoint &tp) const -> std::string;

  public:
    bool isValid = true;
    Event()      = default;
    Event(const std::string &summary, calendar::TimePoint from, calendar::TimePoint till, const std::string &uid);
    Event(const std::string &summary, TimePoint from, TimePoint till, const std::string &uid);

    void setUID(const std::string &property);
    void setSummary(const std::string &property);


@@ 127,8 127,8 @@ class Event

    [[nodiscard]] auto getUID() const -> std::string;
    [[nodiscard]] auto getSummary() const -> std::string;
    [[nodiscard]] auto getDTStartTimePoint() const -> calendar::TimePoint;
    [[nodiscard]] auto getDTEndTimePoint() const -> calendar::TimePoint;
    [[nodiscard]] auto getDTStartTimePoint() const -> TimePoint;
    [[nodiscard]] auto getDTEndTimePoint() const -> TimePoint;

    [[nodiscard]] auto getDTStartString() const -> std::string;
    [[nodiscard]] auto getDTEndString() const -> std::string;

M module-utils/time/TimeRangeParser.cpp => module-utils/time/TimeRangeParser.cpp +2 -2
@@ 19,8 19,8 @@ namespace utils::time
        return utils::localize.get(utils::time::Locale::getPM());
    }

    std::string TimeRangeParser::getCalendarTimeString(calendar::TimePoint startDate,
                                                       calendar::TimePoint endDate,
    std::string TimeRangeParser::getCalendarTimeString(TimePoint startDate,
                                                       TimePoint endDate,
                                                       Version version,
                                                       bool isMode24H)
    {

M module-utils/time/TimeRangeParser.hpp => module-utils/time/TimeRangeParser.hpp +2 -2
@@ 20,8 20,8 @@ namespace utils::time
        std::string AMPMtoString(bool isAm);

      public:
        std::string getCalendarTimeString(calendar::TimePoint startDate,
                                          calendar::TimePoint endDate,
        std::string getCalendarTimeString(TimePoint startDate,
                                          TimePoint endDate,
                                          Version version = Version::normal,
                                          bool isMode24H  = false);
    };

M module-utils/time/time_conversion.hpp => module-utils/time/time_conversion.hpp +5 -0
@@ 226,6 226,11 @@ namespace utils
                return minutes;
            }

            time_t getHours() const
            {
                return hours;
            }

            UTF8 str(DisplayedFormat displayedFormat = DisplayedFormat::Auto0M) const;

            // uses default format

M test/pytest/service-desktop/test_calendar.py => test/pytest/service-desktop/test_calendar.py +1 -0
@@ 4,6 4,7 @@ import pytest
from harness.interface.defs import status
import copy

@pytest.mark.skip("not working on CI")
@pytest.mark.service_desktop_test
def test_calendar(harness):
    # add events