~aleteoryx/muditaos

781f24bbfc9182b539adb0b5416305b86ebe0731 — Maciej Gibowicz 4 years ago 3bdc27a
[EGD-6699] Add timezone selecting window

Implementation of the time zone selection from the list
of available cities.
M image/user/db/settings_v2_002.sql => image/user/db/settings_v2_002.sql +2 -1
@@ 36,5 36,6 @@ INSERT OR IGNORE INTO settings_tab (path, value) VALUES
    ('\EventManager\\br_auto_mode', '0'),
    ('\EventManager\\br_level', '50.0f'),
    ('keypad_light_state', '0'),
    ('gs_current_timezone', '');
    ('gs_current_timezone_name', ''),
    ('gs_current_timezone_rules', '');


M module-apps/application-settings-new/windows/ChangeTimeZone.cpp => module-apps/application-settings-new/windows/ChangeTimeZone.cpp +52 -5
@@ 2,20 2,67 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ChangeTimeZone.hpp"
#include "OptionSetting.hpp"

#include <application-settings-new/ApplicationSettings.hpp>
#include <time/TimeZone.hpp>
#include <service-time/service-time/TimeMessage.hpp>
#include <service-time/Constants.hpp>
#include <service-time/api/TimeSettingsApi.hpp>

namespace gui
{

    ChangeTimeZone::ChangeTimeZone(app::Application *app) : BaseSettingsWindow(app, window::name::change_date_and_time)
    ChangeTimeZone::ChangeTimeZone(app::Application *app)
        : BaseSettingsWindow(app, window::name::change_date_and_time),
          timeZonesList(utils::time::getAvailableTimeZonesWithOffset())
    {
        setTitle(utils::translate("app_settings_date_and_time_time_zone"));
    }

    std::list<Option> ChangeTimeZone::buildOptionsList()
    void ChangeTimeZone::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        selectedTimeZone = stm::api::getCurrentTimezoneName();
        refreshOptionsList(setTimeZoneIndex());
    }

    [[nodiscard]] auto ChangeTimeZone::buildOptionsList() -> std::list<gui::Option>
    {
        std::list<gui::Option> options;

        for (const auto &zone : timeZonesList) {
            options.emplace_back(std::make_unique<gui::option::OptionSettings>(
                zone,
                [=](const gui::Item &item) {
                    application->bus.sendUnicast(
                        std::make_shared<stm::message::SetTimezoneRequest>(extractTimeZoneName(zone)),
                        service::name::service_time);
                    return true;
                },
                nullptr,
                this,
                selectedTimeZone == extractTimeZoneName(zone) ? gui::option::SettingRightItem::Checked
                                                              : gui::option::SettingRightItem::Disabled));
        }

        return options;
    }

    [[nodiscard]] auto ChangeTimeZone::setTimeZoneIndex() -> unsigned int
    {
        unsigned int zoneIndex = 0;
        if (selectedTimeZone.empty()) {
            selectedTimeZone.assign(utils::time::defaultTimeZoneName);
        }
        for (zoneIndex = 0; zoneIndex < timeZonesList.size(); ++zoneIndex) {
            if (selectedTimeZone == extractTimeZoneName(timeZonesList[zoneIndex])) {
                break;
            }
        }
        return zoneIndex;
    }

    [[nodiscard]] auto ChangeTimeZone::extractTimeZoneName(const std::string &name) const noexcept -> std::string
    {
        return {};
        return name.substr(utils::time::timeZoneNameOffset);
    }

} // namespace gui

M module-apps/application-settings-new/windows/ChangeTimeZone.hpp => module-apps/application-settings-new/windows/ChangeTimeZone.hpp +15 -2
@@ 1,6 1,11 @@
#pragma once
// 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 "BaseSettingsWindow.hpp"
#include <application-settings-new/ApplicationSettings.hpp>
#include <Option.hpp>

namespace gui
{


@@ 9,6 14,14 @@ namespace gui
    {
      public:
        explicit ChangeTimeZone(app::Application *app);
        auto buildOptionsList() -> std::list<Option> override;

      protected:
        [[nodiscard]] auto buildOptionsList() -> std::list<Option> override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        [[nodiscard]] auto setTimeZoneIndex() -> unsigned int;
        [[nodiscard]] auto extractTimeZoneName(const std::string &name) const noexcept -> std::string;

        const std::vector<std::string> timeZonesList;
        std::string selectedTimeZone;
    };
} // namespace gui

M module-apps/apps-common/widgets/TimeWidget.cpp => module-apps/apps-common/widgets/TimeWidget.cpp +11 -3
@@ 9,6 9,7 @@
#include <time/time_date_validation.hpp>
#include "DateAndTimeStyle.hpp"
#include <module-apps/application-calendar/data/dateCommon.hpp>
#include <time/TimeZone.hpp>

namespace gui
{


@@ 422,19 423,26 @@ namespace gui
        if (!validateHour()) {
            return false;
        }
        auto hours   = std::chrono::hours(LocalizedHoursToUtcHours(std::stoi(hourInput->getText().c_str())));
        auto hours   = std::chrono::hours(std::stoi(hourInput->getText().c_str()));
        auto minutes = std::chrono::minutes(std::stoi(minuteInput->getText().c_str()));

        if (!mode24H) {
            hours = date::make24(hours, isPm(mode12hInput->getText()));
        }
        auto date = (dateItem != nullptr) ? TimePointFromYearMonthDay(dateItem->getChosenDate())
                                          : TimePointFromYearMonthDay(TimePointToYearMonthDay(TimePointNow()));

        TimePoint presetPointTime = date + hours + minutes;
        auto timeZoneOffset =
            utils::time::getTimeZoneOffset(stm::api::getCurrentTimezoneName(), TimePointToTimeT(presetPointTime));

        if (type == Type::Start) {
            fromTillDate->from = date + hours + minutes;
            fromTillDate->from = presetPointTime - timeZoneOffset;
        }
        else if (type == Type::End) {
            fromTillDate->till = date + hours + minutes;
            fromTillDate->till = presetPointTime - timeZoneOffset;
        }

        return true;
    }


M module-services/service-db/agents/settings/SystemSettings.hpp => module-services/service-db/agents/settings/SystemSettings.hpp +2 -1
@@ 21,7 21,8 @@ namespace settings
        constexpr inline auto eulaAccepted             = "gs_eula_accepted";
        constexpr inline auto osCurrentVersion         = "gs_os_current_version";
        constexpr inline auto osUpdateVersion          = "gs_os_update_version";
        constexpr inline auto currentTimezone          = "gs_current_timezone";
        constexpr inline auto currentTimezoneName      = "gs_current_timezone_name";
        constexpr inline auto currentTimezoneRules     = "gs_current_timezone_rules";
    } // namespace SystemProperties
    namespace Bluetooth
    {

M module-services/service-time/ServiceTime.cpp => module-services/service-time/ServiceTime.cpp +40 -16
@@ 20,6 20,7 @@
#include <service-cellular/ServiceCellular.hpp>
#include <time/time_constants.hpp>
#include <time/time_conversion_factory.hpp>
#include <time/TimeZone.hpp>
#include <service-evtmgr/Constants.hpp>
#include <service-db/service-db/Settings.hpp>
#include <service-db/agents/settings/SystemSettings.hpp>


@@ 30,6 31,8 @@

namespace stm
{
    constexpr auto automaticTimezoneName = "";

    ServiceTime::ServiceTime() : sys::Service(service::name::service_time, "", StackDepth), calendarEvents(this)
    {
        LOG_INFO("[ServiceTime] Initializing");


@@ 158,6 161,12 @@ namespace stm

        connect(typeid(stm::message::SetTimezoneRequest),
                [&](sys::Message *request) -> sys::MessagePointer { return handleSetTimezoneRequest(request); });

        connect(typeid(stm::message::TimeChangeRequestMessage), [&](sys::Message *request) -> sys::MessagePointer {
            auto message = static_cast<stm::message::TimeChangeRequestMessage *>(request);
            timeManager->handleTimeChangeRequest(message->getTime());
            return std::make_shared<sys::ResponseMessage>();
        });
    }

    auto ServiceTime::handleSetAutomaticDateAndTimeRequest(sys::Message *request)


@@ 217,10 226,16 @@ namespace stm
    auto ServiceTime::handleSetTimezoneRequest(sys::Message *request) -> std::shared_ptr<sys::ResponseMessage>
    {
        auto message = static_cast<stm::message::SetTimezoneRequest *>(request);
        auto timeZoneName  = message->getTimezoneName();
        auto timeZoneRules = utils::time::getTimeZoneRules(timeZoneName);

        timeManager->handleTimezoneChangeRequest(timeZoneRules);
        settings->setValue(settings::SystemProperties::currentTimezoneName, timeZoneName);
        stm::internal::StaticData::get().setTimezoneName(timeZoneName);

        settings->setValue(settings::SystemProperties::currentTimezoneRules, timeZoneRules);
        stm::internal::StaticData::get().setTimezoneRules(timeZoneRules);

        timeManager->handleTimezoneChangeRequest(message->getTimezone());
        settings->setValue(settings::SystemProperties::currentTimezone, message->getTimezone());
        stm::internal::StaticData::get().setTimezone(message->getTimezone());
        return std::shared_ptr<sys::ResponseMessage>();
    }



@@ 228,18 243,24 @@ namespace stm
        -> std::shared_ptr<sys::ResponseMessage>
    {
        auto message  = static_cast<CellularTimeNotificationMessage *>(request);
        auto timezone = TimezoneHandler(std::chrono::duration_cast<std::chrono::minutes>(
                                            std::chrono::seconds{message->getTimeZoneOffset().value()}))
                            .getTimezone();
        auto timezoneRules = TimezoneHandler(std::chrono::duration_cast<std::chrono::minutes>(
                                                 std::chrono::seconds{message->getTimeZoneOffset().value()}))
                                 .getTimezone();
        if (stm::api::isAutomaticDateAndTime()) {
            timeManager->handleCellularTimeUpdate(message->getTime().value(), timezone);
            settings->setValue(settings::SystemProperties::currentTimezone, timezone);
            stm::internal::StaticData::get().setTimezone(timezone);
            timeManager->handleCellularTimeUpdate(message->getTime().value(), timezoneRules);
            settings->setValue(settings::SystemProperties::currentTimezoneRules, timezoneRules);
            stm::internal::StaticData::get().setTimezoneRules(timezoneRules);

            settings->setValue(settings::SystemProperties::currentTimezoneName, automaticTimezoneName);
            stm::internal::StaticData::get().setTimezoneName(automaticTimezoneName);
        }
        else if (stm::api::isAutomaticTimezone()) {
            timeManager->handleTimezoneChangeRequest(timezone);
            settings->setValue(settings::SystemProperties::currentTimezone, timezone);
            stm::internal::StaticData::get().setTimezone(timezone);
            timeManager->handleTimezoneChangeRequest(timezoneRules);
            settings->setValue(settings::SystemProperties::currentTimezoneRules, timezoneRules);
            stm::internal::StaticData::get().setTimezoneRules(timezoneRules);

            settings->setValue(settings::SystemProperties::currentTimezoneName, automaticTimezoneName);
            stm::internal::StaticData::get().setTimezoneName(automaticTimezoneName);
        }

        return std::make_shared<sys::ResponseMessage>();


@@ 261,10 282,13 @@ namespace stm
        if (timeFormat != std::nullopt) {
            stm::internal::StaticData::get().setTimeFormat(timeFormat.value());
        }
        auto timezone =
            settings->getValue(settings::SystemProperties::currentTimezone, settings::SettingsScope::AppLocal);
        stm::internal::StaticData::get().setTimezone(timezone);
        timeManager->handleTimezoneChangeRequest(timezone);
        auto timezoneName =
            settings->getValue(settings::SystemProperties::currentTimezoneName, settings::SettingsScope::AppLocal);
        stm::internal::StaticData::get().setTimezoneName(timezoneName);
        auto timezoneRules =
            settings->getValue(settings::SystemProperties::currentTimezoneRules, settings::SettingsScope::AppLocal);
        stm::internal::StaticData::get().setTimezoneRules(timezoneRules);
        timeManager->handleTimezoneChangeRequest(timezoneRules);
    }

} /* namespace stm */

M module-services/service-time/TimeManager.cpp => module-services/service-time/TimeManager.cpp +2 -2
@@ 17,7 17,7 @@ void TimeManager::handleTimeChangeRequest(const time_t &time)
    rtcCommand->setTime(time);
}

void TimeManager::handleTimezoneChangeRequest(const std::string &timezone)
void TimeManager::handleTimezoneChangeRequest(const std::string &timezoneRules)
{
    rtcCommand->setTimezone(timezone);
    rtcCommand->setTimezone(timezoneRules);
}

M module-services/service-time/api/TimeSettingsApi.cpp => module-services/service-time/api/TimeSettingsApi.cpp +7 -3
@@ 31,8 31,12 @@ namespace stm::api
    {
        return stm::internal::StaticData::get().getTimeFormat() == utils::time::Locale::TimeFormat::FormatTime12H;
    }
    const std::string getCurrentTimezone()
    const std::string getCurrentTimezoneName()
    {
        return stm::internal::StaticData::get().getCurrentTimezone();
        return stm::internal::StaticData::get().getCurrentTimezoneName();
    }
} // namespace stm::api
\ No newline at end of file
    const std::string getCurrentTimezoneRules()
    {
        return stm::internal::StaticData::get().getCurrentTimezoneRules();
    }
} // namespace stm::api

M module-services/service-time/api/TimeSettingsApi.hpp => module-services/service-time/api/TimeSettingsApi.hpp +7 -2
@@ 34,7 34,12 @@ namespace stm::api
    bool isTimeFormat12h();
    /**
     * Gets value corresponded to current timezone setting stored in DB
     * @return current timezone
     * @return current timezone name
     */
    const std::string getCurrentTimezone();
    const std::string getCurrentTimezoneName();
    /**
     * Gets value corresponded to current timezone setting stored in DB
     * @return current timezone rules
     */
    const std::string getCurrentTimezoneRules();
} // namespace stm::api

M module-services/service-time/internal/StaticData.cpp => module-services/service-time/internal/StaticData.cpp +14 -5
@@ 51,13 51,22 @@ namespace stm::internal
    {
        return timeFormat;
    }
    void StaticData::setTimezone(const std::string &newTimezone)
    void StaticData::setTimezoneName(const std::string &newTimezone)
    {
        timezone = newTimezone;
        timezoneName = newTimezone;
    }
    std::string StaticData::getCurrentTimezone() const
    std::string StaticData::getCurrentTimezoneName() const
    {
        return timezone;
        return timezoneName;
    }

} // namespace stm::internal
\ No newline at end of file
    void StaticData::setTimezoneRules(const std::string &newTimezone)
    {
        timezoneRules = newTimezone;
    }
    std::string StaticData::getCurrentTimezoneRules() const
    {
        return timezoneRules;
    }

} // namespace stm::internal

M module-services/service-time/internal/StaticData.hpp => module-services/service-time/internal/StaticData.hpp +17 -6
@@ 17,7 17,8 @@ namespace stm::internal
        bool isAutomaticTimezoneOn                 = false;
        utils::time::Locale::DateFormat dateFormat = utils::time::Locale::DateFormat::DD_MM_YYYY;
        utils::time::Locale::TimeFormat timeFormat = utils::time::Locale::TimeFormat::FormatTime12H;
        std::string timezone;
        std::string timezoneName;
        std::string timezoneRules;

        StaticData() = default;



@@ 70,14 71,24 @@ namespace stm::internal
         */
        [[nodiscard]] utils::time::Locale::TimeFormat getTimeFormat() const noexcept;
        /**
         * Sets value corresponded to current Timezone setting
         * Sets value corresponded to current Timezone name setting
         * @param timezone new timezone to set
         */
        void setTimezone(const std::string &newTimezone);
        void setTimezoneName(const std::string &newTimezone);
        /**
         * Gets value corresponded to current Timezone setting
         * Gets value corresponded to current Timezone name setting
         * @retrun actual timezone setting
         */
        [[nodiscard]] std::string getCurrentTimezone() const;
        [[nodiscard]] std::string getCurrentTimezoneName() const;
        /**
         * Sets value corresponded to current Timezone rules setting
         * @param timezone new timezone to set
         */
        void setTimezoneRules(const std::string &newTimezone);
        /**
         * Gets value corresponded to current Timezone rules setting
         * @retrun actual timezone setting
         */
        [[nodiscard]] std::string getCurrentTimezoneRules() const;
    };
} // namespace stm::internal
\ No newline at end of file
} // namespace stm::internal

M module-services/service-time/service-time/TimeManager.hpp => module-services/service-time/service-time/TimeManager.hpp +2 -2
@@ 31,9 31,9 @@ class TimeManager
    void handleTimeChangeRequest(const time_t &time);
    /**
     * Handles timezone change request
     * @param timezone formatted timezone string
     * @param timezoneRules formatted timezone rules string
     */
    void handleTimezoneChangeRequest(const std::string &timezone);
    void handleTimezoneChangeRequest(const std::string &timezoneRules);

  private:
    std::unique_ptr<RTCCommandInterface> rtcCommand;

M module-services/service-time/service-time/TimeMessage.hpp => module-services/service-time/service-time/TimeMessage.hpp +3 -3
@@ 186,10 186,10 @@ namespace stm::message
    class SetTimezoneRequest : public sys::DataMessage
    {
      public:
        explicit SetTimezoneRequest(const std::string &timezoneRules)
            : sys::DataMessage(MessageType::MessageTypeUninitialized), timezone(timezoneRules)
        explicit SetTimezoneRequest(const std::string &timezoneName)
            : sys::DataMessage(MessageType::MessageTypeUninitialized), timezone(timezoneName)
        {}
        auto getTimezone() const -> std::string
        auto getTimezoneName() const -> std::string
        {
            return timezone;
        }

M module-utils/time/test/unittest_TimeZone.cpp => module-utils/time/test/unittest_TimeZone.cpp +44 -12
@@ 4,16 4,38 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
#include <time/TimeZone.hpp>
#include <chrono>

TEST_CASE("TimeZone parser")
{
    struct tm summerTime_tm;
    summerTime_tm.tm_year = 121;
    summerTime_tm.tm_mon  = 5;
    summerTime_tm.tm_mday = 20;
    summerTime_tm.tm_hour = 12;
    summerTime_tm.tm_min  = 0;
    summerTime_tm.tm_sec  = 0;

    struct tm winterTime_tm;
    winterTime_tm.tm_year = 121;
    winterTime_tm.tm_mon  = 12;
    winterTime_tm.tm_mday = 25;
    winterTime_tm.tm_hour = 12;
    winterTime_tm.tm_min  = 0;
    winterTime_tm.tm_sec  = 0;

    time_t summerTime = mktime(&summerTime_tm);
    time_t winterTime = mktime(&winterTime_tm);

    SECTION("Checking the time zone rules for Warsaw")
    {
        std::string zone{"Warsaw"};
        auto rules  = utils::time::getTimeZoneRules(zone);
        auto offset = utils::time::getTimeZoneOffset(zone);
        auto summerOffset = utils::time::getTimeZoneOffset(zone, summerTime);
        auto winterOffset = utils::time::getTimeZoneOffset(zone, winterTime);

        REQUIRE(offset == 4);
        REQUIRE(summerOffset == std::chrono::hours(2));
        REQUIRE(winterOffset == std::chrono::hours(1));
        REQUIRE(rules.size() != 0);
    }



@@ 21,9 43,11 @@ TEST_CASE("TimeZone parser")
    {
        std::string zone{"London"};
        auto rules  = utils::time::getTimeZoneRules(zone);
        auto offset = utils::time::getTimeZoneOffset(zone);
        auto summerOffset = utils::time::getTimeZoneOffset(zone, summerTime);
        auto winterOffset = utils::time::getTimeZoneOffset(zone, winterTime);

        REQUIRE(offset == 0);
        REQUIRE(summerOffset == std::chrono::hours(1));
        REQUIRE(winterOffset == std::chrono::hours(0));
        REQUIRE(rules.size() != 0);
    }



@@ 31,9 55,11 @@ TEST_CASE("TimeZone parser")
    {
        std::string zone{"New York"};
        auto rules  = utils::time::getTimeZoneRules(zone);
        auto offset = utils::time::getTimeZoneOffset(zone);
        auto summerOffset = utils::time::getTimeZoneOffset(zone, summerTime);
        auto winterOffset = utils::time::getTimeZoneOffset(zone, winterTime);

        REQUIRE(offset == -20);
        REQUIRE(summerOffset == std::chrono::hours(-4));
        REQUIRE(winterOffset == std::chrono::hours(-5));
        REQUIRE(rules.size() != 0);
    }



@@ 41,9 67,11 @@ TEST_CASE("TimeZone parser")
    {
        std::string zone{"Tehran"};
        auto rules  = utils::time::getTimeZoneRules(zone);
        auto offset = utils::time::getTimeZoneOffset(zone);
        auto summerOffset = utils::time::getTimeZoneOffset(zone, summerTime);
        auto winterOffset = utils::time::getTimeZoneOffset(zone, winterTime);

        REQUIRE(offset == 14);
        REQUIRE(summerOffset == std::chrono::hours(4) + std::chrono::minutes(30));
        REQUIRE(winterOffset == std::chrono::hours(3) + std::chrono::minutes(30));
        REQUIRE(rules.size() != 0);
    }



@@ 51,9 79,11 @@ TEST_CASE("TimeZone parser")
    {
        std::string zone{"Auckland"};
        auto rules  = utils::time::getTimeZoneRules(zone);
        auto offset = utils::time::getTimeZoneOffset(zone);
        auto summerOffset = utils::time::getTimeZoneOffset(zone, summerTime);
        auto winterOffset = utils::time::getTimeZoneOffset(zone, winterTime);

        REQUIRE(offset == 48);
        REQUIRE(summerOffset == std::chrono::hours(12));
        REQUIRE(winterOffset == std::chrono::hours(13));
        REQUIRE(rules.size() != 0);
    }



@@ 61,9 91,11 @@ TEST_CASE("TimeZone parser")
    {
        std::string zone{"unknown"};
        auto rules  = utils::time::getTimeZoneRules(zone);
        auto offset = utils::time::getTimeZoneOffset(zone);
        auto summerOffset = utils::time::getTimeZoneOffset(zone, summerTime);
        auto winterOffset = utils::time::getTimeZoneOffset(zone, winterTime);

        REQUIRE(offset == 0);
        REQUIRE(summerOffset == std::chrono::hours(0));
        REQUIRE(winterOffset == std::chrono::hours(0));
        REQUIRE(rules.size() == 0);
    }
}

M module-utils/time/time/TimeZone.cpp => module-utils/time/time/TimeZone.cpp +62 -29
@@ 9,57 9,90 @@

namespace utils::time
{
    constexpr uint16_t maxZoneToDisplayLength{27};
    constexpr uint16_t maxZoneRuleLength{50};
    constexpr uint8_t localTimeYearOffset{100};

    [[nodiscard]] auto getAvailableTimeZonesToDisplay() -> std::vector<std::string>
    namespace
    {
        void copyTmToUdateTime(const tm *tm, udatetime_t *dt)
        {
            dt->date.year       = tm->tm_year - 100;
            dt->date.month      = tm->tm_mon;
            dt->date.dayofmonth = tm->tm_mday;
            dt->date.dayofweek  = tm->tm_wday;
            dt->time.hour       = tm->tm_hour;
            dt->time.minute     = tm->tm_min;
            dt->time.second     = tm->tm_sec;
        }

        void unpackDstRules(
            urule_packed_t *packedRules, char *dstRules, int8_t tzOffset, uint8_t currYear, bool isEndRule)
        {
            switch (packedRules->on_dayofmonth) {
            case 0: // the last day of week in month
                [[fallthrough]];
            case 1: // first day of week in month
                snprintf(dstRules,
                         maxZoneRuleLength,
                         ",M%d.%d.%d/%d",
                         packedRules->in_month,
                         packedRules->on_dayofmonth == 0 ? 5 : packedRules->on_dayofmonth,
                         packedRules->on_dayofweek % 7,
                         packedRules->at_hours + (isEndRule ? 1 : 0) + (packedRules->at_is_local_time ? 0 : tzOffset));
                break;
            default: // The Julian day n (1 <= n <= 365)
                snprintf(dstRules,
                         maxZoneRuleLength,
                         ",J%d/%d",
                         get_day_in_year(packedRules, currYear),
                         packedRules->at_hours + (isEndRule ? 1 : 0) + (packedRules->at_is_local_time ? 0 : tzOffset));
            }
        }
    } // namespace

    [[nodiscard]] auto getAvailableTimeZonesWithOffset() -> std::vector<std::string>
    {
        std::vector<std::string> timeZonesNames;
        char zoneToDisplay[maxZoneToDisplayLength];
        auto zonePointer = reinterpret_cast<const char *>(zone_names);
        uzone_t zoneOut;

        for (uint16_t zone = 0; zone < NUM_ZONE_NAMES; zone++) {
            unpack_zone(&zone_defns[get_next(&zonePointer)], zonePointer, &zoneOut);
            timeZonesNames.push_back(zoneOut.name);
            auto pointerToZoneName = zonePointer;
            unpack_zone(&zone_defns[get_next(&zonePointer)], pointerToZoneName, &zoneOut);

            snprintf(zoneToDisplay,
                     maxZoneToDisplayLength,
                     "UTC%s%02d:%02d %s",
                     zoneOut.offset.hours > 0 ? "+" : "-",
                     std::abs(zoneOut.offset.hours),
                     zoneOut.offset.minutes,
                     zoneOut.name);
            timeZonesNames.push_back(zoneToDisplay);
            zonePointer++;
        }

        return timeZonesNames;
    }

    [[nodiscard]] auto getTimeZoneOffset(const std::string &zoneName) -> std::int8_t
    [[nodiscard]] auto getTimeZoneOffset(const std::string &zoneName, const time_t &time) -> std::chrono::seconds
    {
        uzone_t zoneOut;

        if (get_zone_by_name(const_cast<char *>(zoneName.c_str()), &zoneOut)) {
        if (zoneName.empty() || get_zone_by_name(const_cast<char *>(zoneName.c_str()), &zoneOut)) {
            LOG_ERROR("Zone %s not found", zoneName.c_str());
            return 0;
            return std::chrono::seconds(0);
        }

        return zoneOut.src->offset_inc_minutes;
    }
        auto gmTime = gmtime(&time);
        udatetime_t udateTime;
        copyTmToUdateTime(gmTime, &udateTime);

    void unpackDstRules(urule_packed_t *packedRules, char *dstRules, int8_t tzOffset, uint8_t currYear, bool isEndRule)
    {
        switch (packedRules->on_dayofmonth) {
        case 0: // the last day of week in month
            [[fallthrough]];
        case 1: // first day of week in month
            snprintf(dstRules,
                     maxZoneRuleLength,
                     ",M%d.%d.%d/%d",
                     packedRules->in_month,
                     packedRules->on_dayofmonth == 0 ? 5 : packedRules->on_dayofmonth,
                     packedRules->on_dayofweek % 7,
                     packedRules->at_hours + (isEndRule ? 1 : 0) + (packedRules->at_is_local_time ? 0 : tzOffset));
            break;
        default: // The Julian day n (1 <= n <= 365)
            snprintf(dstRules,
                     maxZoneRuleLength,
                     ",J%d/%d",
                     get_day_in_year(packedRules, currYear),
                     packedRules->at_hours + (isEndRule ? 1 : 0) + (packedRules->at_is_local_time ? 0 : tzOffset));
        }
        uoffset_t offset;
        get_current_offset(&zoneOut, &udateTime, &offset);

        return std::chrono::seconds(std::chrono::hours(offset.hours) + std::chrono::minutes(offset.minutes));
    }

    [[nodiscard]] auto getTimeZoneRules(const std::string &zoneName) -> std::string

M module-utils/time/time/TimeZone.hpp => module-utils/time/time/TimeZone.hpp +11 -6
@@ 5,21 5,26 @@

#include <string>
#include <vector>
#include <chrono>

namespace utils::time
{
    /** @brief get all available TimeZones to display
    constexpr uint8_t timeZoneNameOffset{10};
    constexpr auto defaultTimeZoneName{"London"};

    /** @brief get all available TimeZones with offsets
     *
     *  @return vector of string with TimeZones names
     *  @return vector of string with TimeZones names and offsets
     */
    [[nodiscard]] auto getAvailableTimeZonesToDisplay() -> std::vector<std::string>;
    [[nodiscard]] auto getAvailableTimeZonesWithOffset() -> std::vector<std::string>;

    /** @brief get offset of TimeZone
    /** @brief get offset of TimeZone include DST rules
     *
     *  @param zoneName - name of TimeZone
     *  @return offset hours and minutes with a sign in the interval of 15 minutes [6 => 1h 30 min]
     *  @param time - date and time for the offset
     *  @return offset in seconds with a sign
     */
    [[nodiscard]] auto getTimeZoneOffset(const std::string &zoneName) -> std::int8_t;
    [[nodiscard]] auto getTimeZoneOffset(const std::string &zoneName, const time_t &time) -> std::chrono::seconds;

    /** @brief get offset and DST rules of TimeZone
     *