M .gitmodules => .gitmodules +3 -0
@@ 86,3 86,6 @@
path = third-party/re2/src
url = ../re2.git
branch = rt1051
+[submodule "third-party/utz/src/utz"]
+ path = third-party/utz/src/utz
+ url = https://github.com/mudita/utz.git
M module-utils/time/CMakeLists.txt => module-utils/time/CMakeLists.txt +2 -0
@@ 8,6 8,7 @@ target_sources(time
time/time_conversion.cpp
time/time_date_validation.cpp
time/TimeRangeParser.cpp
+ time/TimeZone.cpp
)
target_include_directories(time PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
@@ 17,6 18,7 @@ target_link_libraries(time
log
module-utils
utf8
+ utz::utz
)
if (${ENABLE_TESTS})
M module-utils/time/test/CMakeLists.txt => module-utils/time/test/CMakeLists.txt +9 -0
@@ 7,3 7,12 @@ add_catch2_executable(
module-utils
time
)
+
+add_catch2_executable(
+ NAME
+ utils-TimeZone
+ SRCS
+ unittest_TimeZone.cpp
+ LIBS
+ time
+)<
\ No newline at end of file
A module-utils/time/test/unittest_TimeZone.cpp => module-utils/time/test/unittest_TimeZone.cpp +69 -0
@@ 0,0 1,69 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#define CATCH_CONFIG_MAIN
+#include <catch2/catch.hpp>
+#include <time/TimeZone.hpp>
+
+TEST_CASE("TimeZone parser")
+{
+ SECTION("Checking the time zone rules for Warsaw")
+ {
+ std::string zone{"Warsaw"};
+ auto rules = utils::time::getTimeZoneRules(zone);
+ auto offset = utils::time::getTimeZoneOffset(zone);
+
+ REQUIRE(offset == 4);
+ REQUIRE(rules.size() != 0);
+ }
+
+ SECTION("Checking the time zone rules for London")
+ {
+ std::string zone{"London"};
+ auto rules = utils::time::getTimeZoneRules(zone);
+ auto offset = utils::time::getTimeZoneOffset(zone);
+
+ REQUIRE(offset == 0);
+ REQUIRE(rules.size() != 0);
+ }
+
+ SECTION("Checking the time zone rules for New York")
+ {
+ std::string zone{"New York"};
+ auto rules = utils::time::getTimeZoneRules(zone);
+ auto offset = utils::time::getTimeZoneOffset(zone);
+
+ REQUIRE(offset == -20);
+ REQUIRE(rules.size() != 0);
+ }
+
+ SECTION("Checking the time zone rules for Tehran")
+ {
+ std::string zone{"Tehran"};
+ auto rules = utils::time::getTimeZoneRules(zone);
+ auto offset = utils::time::getTimeZoneOffset(zone);
+
+ REQUIRE(offset == 14);
+ REQUIRE(rules.size() != 0);
+ }
+
+ SECTION("Checking the time zone rules for Auckland")
+ {
+ std::string zone{"Auckland"};
+ auto rules = utils::time::getTimeZoneRules(zone);
+ auto offset = utils::time::getTimeZoneOffset(zone);
+
+ REQUIRE(offset == 48);
+ REQUIRE(rules.size() != 0);
+ }
+
+ SECTION("Checking the time zone rules for unknown zone")
+ {
+ std::string zone{"unknown"};
+ auto rules = utils::time::getTimeZoneRules(zone);
+ auto offset = utils::time::getTimeZoneOffset(zone);
+
+ REQUIRE(offset == 0);
+ REQUIRE(rules.size() == 0);
+ }
+}
A module-utils/time/time/TimeZone.cpp => module-utils/time/time/TimeZone.cpp +97 -0
@@ 0,0 1,97 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "TimeZone.hpp"
+#include <utz/utz.h>
+#include <utz/zones.h>
+#include <log/log.hpp>
+#include <ctime>
+
+namespace utils::time
+{
+ constexpr uint16_t maxZoneRuleLength{50};
+ constexpr uint8_t localTimeYearOffset{100};
+
+ [[nodiscard]] auto getAvailableTimeZonesToDisplay() -> std::vector<std::string>
+ {
+ std::vector<std::string> timeZonesNames;
+ 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);
+ zonePointer++;
+ }
+
+ return timeZonesNames;
+ }
+
+ [[nodiscard]] auto getTimeZoneOffset(const std::string &zoneName) -> std::int8_t
+ {
+ uzone_t zoneOut;
+
+ if (get_zone_by_name(const_cast<char *>(zoneName.c_str()), &zoneOut)) {
+ LOG_ERROR("Zone %s not found", zoneName.c_str());
+ return 0;
+ }
+
+ return zoneOut.src->offset_inc_minutes;
+ }
+
+ 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));
+ }
+ }
+
+ [[nodiscard]] auto getTimeZoneRules(const std::string &zoneName) -> std::string
+ {
+ std::string zoneRules;
+ char offsetRules[maxZoneRuleLength];
+ urule_packed_t startRules, endRules;
+ uzone_t zoneOut;
+ time_t tm = std::time(nullptr);
+ uint8_t currentYear = localtime(&tm)->tm_year - localTimeYearOffset;
+
+ if (get_zone_by_name(const_cast<char *>(zoneName.c_str()), &zoneOut)) {
+ LOG_ERROR("Zone %s not found", zoneName.c_str());
+ return "";
+ }
+
+ snprintf(offsetRules, maxZoneRuleLength, "UTC%d:%02d", -zoneOut.offset.hours, zoneOut.offset.minutes);
+
+ zoneRules = offsetRules;
+
+ if (!get_zone_rules(zoneOut.src, currentYear, &startRules, &endRules)) {
+ char dstRules[maxZoneRuleLength];
+
+ zoneRules.append("UTC");
+ unpackDstRules(&startRules, dstRules, zoneOut.offset.hours, currentYear, false);
+ zoneRules.append(dstRules);
+
+ unpackDstRules(&endRules, dstRules, zoneOut.offset.hours, currentYear, true);
+ zoneRules.append(dstRules);
+ }
+
+ return zoneRules;
+ }
+
+} // namespace utils::time
A module-utils/time/time/TimeZone.hpp => module-utils/time/time/TimeZone.hpp +31 -0
@@ 0,0 1,31 @@
+// 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 <string>
+#include <vector>
+
+namespace utils::time
+{
+ /** @brief get all available TimeZones to display
+ *
+ * @return vector of string with TimeZones names
+ */
+ [[nodiscard]] auto getAvailableTimeZonesToDisplay() -> std::vector<std::string>;
+
+ /** @brief get offset of TimeZone
+ *
+ * @param zoneName - name of TimeZone
+ * @return offset hours and minutes with a sign in the interval of 15 minutes [6 => 1h 30 min]
+ */
+ [[nodiscard]] auto getTimeZoneOffset(const std::string &zoneName) -> std::int8_t;
+
+ /** @brief get offset and DST rules of TimeZone
+ *
+ * @param zoneName - name of TimeZone
+ * @return string with rules to set TimeZone
+ */
+ [[nodiscard]] auto getTimeZoneRules(const std::string &zoneName) -> std::string;
+
+} // namespace utils::time
M third-party/CMakeLists.txt => third-party/CMakeLists.txt +1 -0
@@ 13,6 13,7 @@ add_subdirectory(littlefs)
add_subdirectory(json)
add_subdirectory(gsl)
add_subdirectory(re2)
+add_subdirectory(utz)
if (${PROJECT_TARGET} STREQUAL "TARGET_RT1051")
add_subdirectory(CrashDebug)
endif()
A third-party/utz/CMakeLists.txt => third-party/utz/CMakeLists.txt +17 -0
@@ 0,0 1,17 @@
+
+add_library(utz)
+add_library(utz::utz ALIAS utz)
+target_sources(utz
+ PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/utz/utz.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/utz/zones.c
+ PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/utz/utz.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/utz/zones.h
+)
+
+target_include_directories(utz
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
+)
+
A third-party/utz/src/utz => third-party/utz/src/utz +1 -0
@@ 0,0 1,1 @@
+Subproject commit d3da89075cc344d5763d2abd294b67c326abeaec