From 25876421ecfaaa714fdb7f066f33878d7d2a0ae3 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Tue, 8 Sep 2026 21:34:07 +0300 Subject: [PATCH 1/6] fix(review): address v1.0.6 audit findings Keep convenience aliases and the flat parser dispatch unchanged while fixing confirmed defects in constants, Release test assertions, installation configuration, documentation metadata, comments, include guards, and MQL5 manual test reporting. Add repository guidance for review and validation workflows. --- .github/workflows/publish.yaml | 8 ++- CHANGELOG.md | 2 +- CMakeLists.txt | 11 +++- Doxyfile | 2 +- MQL5/Include/time_shield/constants.mqh | 7 ++- .../time_shield/tests/test_eet_to_gmt.mq5 | 11 +++- .../tests/test_time_zone_matrix.mq5 | 15 ++++- docs/groups.dox | 2 +- docs/groups_mql5.dox | 2 +- docs/mainpage.md | 2 +- include/time_shield.hpp | 2 +- include/time_shield/TimerScheduler.hpp | 6 +- include/time_shield/ZonedClock.hpp | 2 +- include/time_shield/constants.hpp | 2 +- include/time_shield/date_time_conversions.hpp | 62 +++++++++---------- include/time_shield/ntp_client.hpp | 2 +- .../time_shield/time_conversion_aliases.hpp | 8 +-- include/time_shield/time_formatting.hpp | 4 +- include/time_shield/time_utils.hpp | 2 +- include/time_shield/unix_time_conversions.hpp | 6 +- include/time_shield/validation.hpp | 6 +- tests/odr/CMakeLists.txt | 7 +++ tests/time_conversions_coverage_test.cpp | 3 + 23 files changed, 105 insertions(+), 69 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 9882fbb5..a467f50c 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -15,13 +15,15 @@ jobs: with: submodules: "true" fetch-depth: 0 - - name: Inject version into mainpage.md + - name: Inject version into documentation sources run: | - TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "0.0.0-untagged") + TAG=$(git describe --tags --abbrev=0 2>/dev/null || sed -n 's/^project(TimeShield VERSION \([^ ]*\).*/\1/p' CMakeLists.txt) + test -n "$TAG" || { echo "Unable to determine project version"; exit 1; } TAG=$(echo "$TAG" | sed 's/^v//') echo "Using version: $TAG" test -f docs/mainpage.md || { echo "mainpage.md not found!"; exit 1; } - sed -i "0,/VERSION_PLACEHOLDER/s//$TAG/" docs/mainpage.md + sed -i -E "s/^(\\*\\*Version:\*\\* ).*/\\1\\`$TAG\\`/" docs/mainpage.md + sed -i -E "s/^PROJECT_NUMBER[[:space:]]*=.*/PROJECT_NUMBER = $TAG/" Doxyfile - name: Generate Documentation uses: mattnotmitt/doxygen-action@edge - name: Publish generated content to GitHub Pages diff --git a/CHANGELOG.md b/CHANGELOG.md index 43c905c9..7e7bbec6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. -## [v1.0.6] - 2026-04-23 +## [v1.0.6] - Unreleased - Added `ZonedClock` with reusable named-zone and fixed-offset local-time helpers and clarified timezone semantics. - Completed ISO week-date parsing support and formatter/parser round-trip coverage. - Added timeframe parsing helpers for trading and engineering strings in C++ and MQL5, with docs, examples, and tests. diff --git a/CMakeLists.txt b/CMakeLists.txt index a59c9ea8..83e9c698 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,9 @@ if(NOT DEFINED CMAKE_CXX_STANDARD) endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + add_library(time_shield INTERFACE) add_library(time_shield::time_shield ALIAS time_shield) @@ -18,9 +21,6 @@ target_include_directories( $ ) -include(GNUInstallDirs) -include(CMakePackageConfigHelpers) - set(TIME_SHIELD_ENABLE_NTP_CLIENT_DEFAULT ON) option(TIME_SHIELD_ENABLE_NTP_CLIENT "Enable NTP client" ${TIME_SHIELD_ENABLE_NTP_CLIENT_DEFAULT}) @@ -79,8 +79,10 @@ option(TIME_SHIELD_CPP_BUILD_EXAMPLES "Build examples" ${is_top_level}) option(TIME_SHIELD_CPP_BUILD_TESTS "Build tests" ${is_top_level}) if(MSVC) set(COMMON_WARN_FLAGS /W4 /wd4996) + set(TEST_ASSERT_FLAGS /UNDEBUG) else() set(COMMON_WARN_FLAGS -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wshadow) + set(TEST_ASSERT_FLAGS -UNDEBUG) endif() file(GLOB_RECURSE PROJECT_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} include/*.hpp) @@ -110,6 +112,9 @@ if(TIME_SHIELD_CPP_BUILD_TESTS) if(COMMON_WARN_FLAGS) target_compile_options(${test_name} PRIVATE ${COMMON_WARN_FLAGS}) endif() + if(TEST_ASSERT_FLAGS) + target_compile_options(${test_name} PRIVATE ${TEST_ASSERT_FLAGS}) + endif() add_test(NAME ${test_name} COMMAND ${test_name}) endforeach() diff --git a/Doxyfile b/Doxyfile index 8ce3623f..8cd3d462 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = "Time Shield Library" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = 1.0.6 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/MQL5/Include/time_shield/constants.mqh b/MQL5/Include/time_shield/constants.mqh index 6d3d92f8..ff9a811e 100644 --- a/MQL5/Include/time_shield/constants.mqh +++ b/MQL5/Include/time_shield/constants.mqh @@ -137,7 +137,7 @@ namespace time_shield { const long MIN_PER_WEEK = 10080; ///< Minutes per week const long MIN_PER_10_DAY = 10*1440; ///< Minutes per 10 day const long MIN_PER_15_DAY = 15*1440; ///< Minutes per 15 day - const long MIN_PER_30_DAY = 15*1440; ///< Minutes per 30 day + const long MIN_PER_30_DAY = 30*1440; ///< Minutes per 30 day const long MIN_PER_MONTH = 40320; ///< Minutes per month (28 days) const long MAX_MOON_MIN = 42523; ///< Maximum lunar minutes @@ -160,7 +160,10 @@ namespace time_shield { const long MAX_YEAR = 292277022000; ///< Maximum representable year const long MIN_YEAR = -2967369602200; ///< Minimum representable year const long ERROR_YEAR = 9223372036854770000; ///< Error year value - const long MAX_TIMESTAMP = 9223371890843040000; ///< Maximum timestamp value + const long MAX_TIMESTAMP = 9223372005318775; ///< Maximum timestamp value in seconds + const long MIN_TIMESTAMP = -9223372005318775; ///< Minimum timestamp value in seconds + const long MAX_TIMESTAMP_MS = 9223372005318775999; ///< Maximum timestamp value in milliseconds + const long MIN_TIMESTAMP_MS = -9223372005318775000; ///< Minimum timestamp value in milliseconds const long ERROR_TIMESTAMP = 9223372036854770000; ///< Error timestamp value const double MAX_OADATE = 1.7976931348623158e+308; ///< Maximum representable oadate_t value const double AVG_DAYS_PER_YEAR = 365.25; ///< Average days per year diff --git a/MQL5/Scripts/time_shield/tests/test_eet_to_gmt.mq5 b/MQL5/Scripts/time_shield/tests/test_eet_to_gmt.mq5 index 0efdacd7..a71acf9c 100644 --- a/MQL5/Scripts/time_shield/tests/test_eet_to_gmt.mq5 +++ b/MQL5/Scripts/time_shield/tests/test_eet_to_gmt.mq5 @@ -11,6 +11,7 @@ void OnStart() { time_shield::init(); + bool is_ok = true; // Winter timestamp: 2024-01-15 12:00:00 EET datetime winter_eet = (datetime)time_shield::to_ts(2024, time_shield::JAN, 15, 12, 0, 0); @@ -19,8 +20,10 @@ void OnStart() { if (winter_gmt == winter_expected) Print("Winter conversion passed"); - else + else { Print("Winter conversion failed: ", winter_gmt, " != ", winter_expected); + is_ok = false; + } // Summer timestamp: 2024-07-15 12:00:00 EET (EEST) datetime summer_eet = (datetime)time_shield::to_ts(2024, time_shield::JUL, 15, 12, 0, 0); @@ -29,7 +32,11 @@ void OnStart() { if(summer_gmt == summer_expected) Print("Summer conversion passed"); - else + else { Print("Summer conversion failed: ", summer_gmt, " != ", summer_expected); + is_ok = false; + } + + Print(is_ok ? "EET to GMT tests passed" : "EET to GMT tests failed"); } diff --git a/MQL5/Scripts/time_shield/tests/test_time_zone_matrix.mq5 b/MQL5/Scripts/time_shield/tests/test_time_zone_matrix.mq5 index 8c341ec7..dcb82167 100644 --- a/MQL5/Scripts/time_shield/tests/test_time_zone_matrix.mq5 +++ b/MQL5/Scripts/time_shield/tests/test_time_zone_matrix.mq5 @@ -11,25 +11,34 @@ void OnStart() { time_shield::init(); + bool is_ok = true; datetime ist_local = (datetime)time_shield::to_ts(2024, time_shield::JUL, 15, 12, 0, 0); datetime expected_gmt = (datetime)time_shield::to_ts(2024, time_shield::JUL, 15, 6, 30, 0); datetime gmt_from_ist = time_shield::ist_to_gmt(ist_local); if(gmt_from_ist == expected_gmt) Print("IST -> GMT passed"); - else + else { Print("IST -> GMT failed: ", gmt_from_ist, " != ", expected_gmt); + is_ok = false; + } datetime myt_from_ist = time_shield::convert_time_zone(ist_local, time_shield::IST, time_shield::MYT); datetime expected_myt = (datetime)time_shield::to_ts(2024, time_shield::JUL, 15, 14, 30, 0); if(myt_from_ist == expected_myt) Print("IST -> MYT passed"); - else + else { Print("IST -> MYT failed: ", myt_from_ist, " != ", expected_myt); + is_ok = false; + } datetime kyiv_local = (datetime)time_shield::to_ts(2024, time_shield::JUL, 15, 12, 0, 0); if(time_shield::kyiv_to_gmt(kyiv_local) == time_shield::eet_to_gmt(kyiv_local)) Print("Kyiv alias passed"); - else + else { Print("Kyiv alias failed"); + is_ok = false; + } + + Print(is_ok ? "Time zone matrix tests passed" : "Time zone matrix tests failed"); } diff --git a/docs/groups.dox b/docs/groups.dox index c6e38c2c..42ceda94 100644 --- a/docs/groups.dox +++ b/docs/groups.dox @@ -106,7 +106,7 @@ for working with date-time structures, UNIX days, and various time units (hours, - Determine the start of the current year: \code{.cpp} - auto year_start = time_shield::start_of_year(current_timestamp()); + auto year_start = time_shield::start_of_year(time_shield::ts()); \endcode */ diff --git a/docs/groups_mql5.dox b/docs/groups_mql5.dox index ac4dffa5..06d0aca8 100644 --- a/docs/groups_mql5.dox +++ b/docs/groups_mql5.dox @@ -94,7 +94,7 @@ for working with date-time structures, UNIX days, and various time units (hours, - Determine the start of the current year: \code{.cpp} - auto year_start = time_shield::start_of_year(current_timestamp()); + auto year_start = time_shield::start_of_year(time_shield::ts()); \endcode */ diff --git a/docs/mainpage.md b/docs/mainpage.md index 2228bc6b..af1bc569 100644 --- a/docs/mainpage.md +++ b/docs/mainpage.md @@ -1,6 +1,6 @@ \mainpage Time Shield Library -**Version:** `VERSION_PLACEHOLDER` +**Version:** 1.0.6 \section intro_sec Introduction diff --git a/include/time_shield.hpp b/include/time_shield.hpp index 64f0fec1..bbab8b18 100644 --- a/include/time_shield.hpp +++ b/include/time_shield.hpp @@ -53,7 +53,7 @@ namespace tshield = time_shield; /// \brief Main namespace for the Time Shield library. /// \details /// Contains all public types, constants, and functions of the library. -/// The API provides: +/// API coverage includes: /// - time and date structures /// - parsing and formatting /// - conversions between representations (seconds/milliseconds, calendar fields, etc.) diff --git a/include/time_shield/TimerScheduler.hpp b/include/time_shield/TimerScheduler.hpp index 7de06754..24972383 100644 --- a/include/time_shield/TimerScheduler.hpp +++ b/include/time_shield/TimerScheduler.hpp @@ -136,7 +136,7 @@ namespace time_shield { /// \brief Processes all timers that are ready to fire at the moment of the call. /// - /// The method is non-blocking: it does not wait for future timers. + /// Method is non-blocking and does not wait for future timers. /// It must not be called while the worker thread started by run() is /// active. void process(); @@ -204,7 +204,7 @@ namespace time_shield { /// \brief Stops the timer. /// - /// The operation is non-blocking: the method does not wait for a + /// Operation is non-blocking and does not wait for a /// running callback to finish. Use stop_and_wait() to synchronously /// wait for completion. void stop(); @@ -231,7 +231,7 @@ namespace time_shield { /// \brief Creates a single-shot timer that invokes the callback once. /// - /// The helper keeps the timer alive until the callback finishes. + /// Helper keeps the timer alive until the callback finishes. template static void single_shot(TimerScheduler& scheduler, std::chrono::duration interval, diff --git a/include/time_shield/ZonedClock.hpp b/include/time_shield/ZonedClock.hpp index 4c292712..0bc6662e 100644 --- a/include/time_shield/ZonedClock.hpp +++ b/include/time_shield/ZonedClock.hpp @@ -27,7 +27,7 @@ namespace time_shield { /// \brief Stores a target local-time context backed by a named zone or fixed UTC offset. /// - /// The class resolves the effective offset on demand. Named zones are recalculated + /// Class resolves the effective offset on demand. Named zones are recalculated /// for the requested UTC instant, while numeric offsets remain fixed. Current UTC time /// can come from the local realtime clock or from the global NTP service. class ZonedClock final { diff --git a/include/time_shield/constants.hpp b/include/time_shield/constants.hpp index b87ec308..7036075b 100644 --- a/include/time_shield/constants.hpp +++ b/include/time_shield/constants.hpp @@ -131,7 +131,7 @@ namespace time_shield { constexpr int64_t MIN_PER_WEEK = 10080; ///< Minutes per week constexpr int64_t MIN_PER_10_DAY = 10*1440; ///< Minutes per 10 day constexpr int64_t MIN_PER_15_DAY = 15*1440; ///< Minutes per 15 day - constexpr int64_t MIN_PER_30_DAY = 15*1440; ///< Minutes per 30 day + constexpr int64_t MIN_PER_30_DAY = 30*1440; ///< Minutes per 30 day constexpr int64_t MIN_PER_MONTH = 40320; ///< Minutes per month (28 days) constexpr int64_t MAX_MOON_MIN = 42523; ///< Maximum lunar minutes diff --git a/include/time_shield/date_time_conversions.hpp b/include/time_shield/date_time_conversions.hpp index 0a4f9681..622dd8b7 100644 --- a/include/time_shield/date_time_conversions.hpp +++ b/include/time_shield/date_time_conversions.hpp @@ -45,9 +45,9 @@ namespace time_shield { /// \return A date-time structure of type T1. template T1 to_date_time(T2 ts) { - // 9223372029693630000 - значение на момент 292277024400 от 2000 года - // Такое значение приводит к неправильному вычислению умножения n_400_years * SEC_PER_400_YEARS - // Поэтому пришлось снизить до 9223371890843040000 + // 9223372029693630000 reaches year 292277024400 from the 2000 epoch. + // This value overflows the n_400_years * SEC_PER_400_YEARS calculation. + // The supported bound is reduced to 9223371890843040000. constexpr int64_t BIAS_292277022000 = 9223371890843040000LL; constexpr int64_t BIAS_2000 = 946684800LL; @@ -86,10 +86,10 @@ namespace time_shield { constexpr int JAN_AND_FEB_DAY_LEAP_YEAR = 60 - 1; constexpr int TABLE_MONTH_OF_YEAR[] = { - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 31 январь - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 28 февраль - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 31 март - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, // 30 апрель + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // January (31 days) + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // February (28 days) + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // March (31 days) + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, // April (30 days) 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, @@ -100,10 +100,10 @@ namespace time_shield { 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, }; constexpr int TABLE_DAY_OF_YEAR[] = { - 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 31 январь - 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28, // 28 февраль - 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 31 март - 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30, // 30 апрель + 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // January (31 days) + 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28, // February (28 days) + 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // March (31 days) + 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30, // April (30 days) 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, @@ -219,7 +219,7 @@ namespace time_shield { /// \throws std::invalid_argument if the date-time combination is invalid. /// /// \par Aliases: - /// The following function names are provided as aliases: + /// Following function names are provided as aliases: /// - `ts(...)` /// - `get_ts(...)` /// - `get_timestamp(...)` @@ -262,9 +262,9 @@ namespace time_shield { secs += years * SEC_PER_YEAR; - // 9223372029693630000 - значение на момент 292277024400 от 2000 года - // Такое значение приводит к неправильному вычислению умножения n_400_years * SEC_PER_400_YEARS - // Поэтому пришлось снизить до 9223371890843040000 + // 9223372029693630000 reaches year 292277024400 from the 2000 epoch. + // This value overflows the n_400_years * SEC_PER_400_YEARS calculation. + // The supported bound is reduced to 9223371890843040000. constexpr int64_t BIAS_292277022000 = 9223371890843040000LL; constexpr int64_t BIAS_2000 = 946684800LL; @@ -332,9 +332,9 @@ namespace time_shield { secs += years * SEC_PER_YEAR; - // 9223372029693630000 - значение на момент 292277024400 от 2000 года - // Такое значение приводит к неправильному вычислению умножения n_400_years * SEC_PER_400_YEARS - // Поэтому пришлось снизить до 9223371890843040000 + // 9223372029693630000 reaches year 292277024400 from the 2000 epoch. + // This value overflows the n_400_years * SEC_PER_400_YEARS calculation. + // The supported bound is reduced to 9223371890843040000. constexpr int64_t BIAS_292277022000 = 9223371890843040000LL; constexpr int64_t BIAS_2000 = 946684800LL; @@ -422,7 +422,7 @@ namespace time_shield { /// \throws std::invalid_argument if the date-time combination is invalid. /// /// \par Aliases: - /// The following function names are provided as aliases: + /// Following function names are provided as aliases: /// - `ts(...)` /// - `get_ts(...)` /// - `get_timestamp(...)` @@ -646,7 +646,7 @@ namespace time_shield { /// \brief Get the start of the day timestamp. /// /// This function returns the timestamp at the start of the day. - /// The function sets the hours, minutes, and seconds to zero. + /// Sets the hours, minutes, and seconds to zero. /// /// \param ts Timestamp. /// \return Start of the day timestamp. @@ -669,7 +669,7 @@ namespace time_shield { /// \brief Get the start of the day timestamp in seconds. /// /// This function returns the timestamp at the start of the day in seconds. - /// The function sets the hours, minutes, and seconds to zero. + /// Sets the hours, minutes, and seconds to zero. /// /// \param ts_ms Timestamp in milliseconds. /// \return Start of the day timestamp in seconds. @@ -680,7 +680,7 @@ namespace time_shield { /// \brief Get the start of the day timestamp in milliseconds. /// /// This function returns the timestamp at the start of the day in milliseconds. - /// The function sets the hours, minutes, seconds, and milliseconds to zero. + /// Sets the hours, minutes, seconds, and milliseconds to zero. /// /// \param ts_ms Timestamp in milliseconds. /// \return Start of the day timestamp in milliseconds. @@ -904,10 +904,10 @@ namespace time_shield { constexpr int JAN_AND_FEB_DAY_LEAP_YEAR = 60; constexpr int TABLE_MONTH_OF_YEAR[] = { 0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 31 январь - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 28 февраль - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 31 март - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, // 30 апрель + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // January (31 days) + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // February (28 days) + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // March (31 days) + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, // April (30 days) 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, @@ -930,13 +930,13 @@ namespace time_shield { template TIME_SHIELD_CONSTEXPR inline T day_of_month(ts_t ts = time_shield::ts()) { constexpr int JAN_AND_FEB_DAY_LEAP_YEAR = 60; - // таблица для обычного года, не високосного + // Month numbers for a common year. constexpr int TABLE_DAY_OF_YEAR[] = { 0, - 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 31 январь - 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28, // 28 февраль - 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 31 март - 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30, // 30 апрель + 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // January (31 days) + 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28, // February (28 days) + 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // March (31 days) + 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30, // April (30 days) 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, diff --git a/include/time_shield/ntp_client.hpp b/include/time_shield/ntp_client.hpp index 398354fd..9f8631aa 100644 --- a/include/time_shield/ntp_client.hpp +++ b/include/time_shield/ntp_client.hpp @@ -6,7 +6,7 @@ /// \file ntp_client.hpp /// \brief Simple NTP client for querying time offset from NTP servers. /// -/// The feature is optional and controlled by `TIME_SHIELD_ENABLE_NTP_CLIENT`. +/// Feature availability is controlled by `TIME_SHIELD_ENABLE_NTP_CLIENT`. /// \ingroup ntp #include "config.hpp" diff --git a/include/time_shield/time_conversion_aliases.hpp b/include/time_shield/time_conversion_aliases.hpp index 367227a7..6891a165 100644 --- a/include/time_shield/time_conversion_aliases.hpp +++ b/include/time_shield/time_conversion_aliases.hpp @@ -4,11 +4,11 @@ #define TIME_SHIELD_HEADER_TIME_SHIELD_TIME_CONVERSION_ALIASES_HPP_INCLUDED /// \file time_conversion_aliases.hpp -/// \brief Doxygen-only stubs for alias functions defined via macros. +/// \brief Convenience aliases for the time-conversion API. /// -/// These declarations are only visible to Doxygen to enable searching and linking -/// to functions like `ts`, `get_ts`, etc., which are defined via macros. -/// This file should be included only at the end of `time_conversion.hpp`. +/// Definitions provide alternative names for commonly used conversion helpers. +/// Doxygen sees the declarations directly and can index each alias independently. +/// Include this header after the canonical conversion declarations. #include diff --git a/include/time_shield/time_formatting.hpp b/include/time_shield/time_formatting.hpp index 86b63f2f..2c1037b1 100644 --- a/include/time_shield/time_formatting.hpp +++ b/include/time_shield/time_formatting.hpp @@ -396,7 +396,7 @@ namespace time_shield { /// This function is similar to the strftime function and supports the majority of its specifiers, /// as well as additional ones: YY, YYYY, YYYYYY, WWW, www, hh, mm, ss, dd, sss. /// - /// The function accepts the following format specifiers as parameters: + /// Accepts the following format specifiers as parameters: /// - %YYYYYY: Year with reduction in the number of millennia. /// - %YYYY: Year represented by 4 digits. /// - %YY: Last two digits of the year. @@ -484,7 +484,7 @@ namespace time_shield { /// This function is similar to the strftime function and supports the majority of its specifiers, /// as well as additional ones: YY, YYYY, YYYYYY, WWW, www, hh, mm, ss, dd, sss. /// - /// The function accepts the following format specifiers as parameters: + /// Accepts the following format specifiers as parameters: /// - %YYYYYY: Year with reduction in the number of millennia. /// - %YYYY: Year represented by 4 digits. /// - %YY: Last two digits of the year. diff --git a/include/time_shield/time_utils.hpp b/include/time_shield/time_utils.hpp index 787c62eb..6ec4fbf9 100644 --- a/include/time_shield/time_utils.hpp +++ b/include/time_shield/time_utils.hpp @@ -41,7 +41,7 @@ namespace time_shield { // https://en.cppreference.com/w/c/chrono/timespec_get struct timespec ts; # if defined(CLOCK_REALTIME) - clock_gettime(CLOCK_REALTIME, &ts); // Версия для POSIX + clock_gettime(CLOCK_REALTIME, &ts); // POSIX implementation # else timespec_get(&ts, TIME_UTC); # endif diff --git a/include/time_shield/unix_time_conversions.hpp b/include/time_shield/unix_time_conversions.hpp index a6251028..f0347ec8 100644 --- a/include/time_shield/unix_time_conversions.hpp +++ b/include/time_shield/unix_time_conversions.hpp @@ -26,9 +26,9 @@ namespace time_shield { /// \return T Year corresponding to the given timestamp. template TIME_SHIELD_CONSTEXPR T years_since_epoch(ts_t ts) noexcept { - // 9223372029693630000 - значение на момент 292277024400 от 2000 года - // Такое значение приводит к неправильному вычислению умножения n_400_years * SEC_PER_400_YEARS - // Поэтому пришлось снизить до 9223371890843040000 + // 9223372029693630000 reaches year 292277024400 from the 2000 epoch. + // This value overflows the n_400_years * SEC_PER_400_YEARS calculation. + // The supported bound is reduced to 9223371890843040000. constexpr int64_t BIAS_292277022000 = 9223371890843040000LL; constexpr int64_t BIAS_2000 = 946684800LL; diff --git a/include/time_shield/validation.hpp b/include/time_shield/validation.hpp index bfc060a3..532917b4 100644 --- a/include/time_shield/validation.hpp +++ b/include/time_shield/validation.hpp @@ -82,9 +82,9 @@ namespace time_shield { /// \param ts Timestamp in seconds since the Unix epoch. /// \return Returns true if the year is a leap year. TIME_SHIELD_CONSTEXPR inline bool is_leap_year_ts(ts_t ts) { - // 9223372029693630000 - значение на момент 292277024400 от 2000 года - // Такое значение приводит к неправильному вычислению умножения n_400_years * SEC_PER_400_YEARS - // Поэтому пришлось снизить до 9223371890843040000 + // 9223372029693630000 reaches year 292277024400 from the 2000 epoch. + // This value overflows the n_400_years * SEC_PER_400_YEARS calculation. + // The supported bound is reduced to 9223371890843040000. constexpr int64_t BIAS_292277022000 = 9223371890843040000LL; constexpr int64_t BIAS_2000 = 946684800LL; diff --git a/tests/odr/CMakeLists.txt b/tests/odr/CMakeLists.txt index b1625f68..cf9182d0 100644 --- a/tests/odr/CMakeLists.txt +++ b/tests/odr/CMakeLists.txt @@ -6,24 +6,28 @@ set(ODR_SOURCES add_executable(odr_cxx11 ${ODR_SOURCES}) target_link_libraries(odr_cxx11 PRIVATE time_shield::time_shield) set_target_properties(odr_cxx11 PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES) +target_compile_options(odr_cxx11 PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME odr_cxx11 COMMAND odr_cxx11) add_executable(cxx11_header_compat cxx11_header_compat.cpp) target_link_libraries(cxx11_header_compat PRIVATE time_shield::time_shield) set_target_properties(cxx11_header_compat PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES) +target_compile_options(cxx11_header_compat PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME cxx11_header_compat COMMAND cxx11_header_compat) add_executable(odr_cxx14 ${ODR_SOURCES}) target_link_libraries(odr_cxx14 PRIVATE time_shield::time_shield) set_target_properties(odr_cxx14 PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED YES) +target_compile_options(odr_cxx14 PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME odr_cxx14 COMMAND odr_cxx14) add_executable(odr_cxx17 ${ODR_SOURCES}) target_link_libraries(odr_cxx17 PRIVATE time_shield::time_shield) set_target_properties(odr_cxx17 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +target_compile_options(odr_cxx17 PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME odr_cxx17 COMMAND odr_cxx17) @@ -35,17 +39,20 @@ set(NTP_TIME_SERVICE_ODR_SOURCES add_executable(ntp_time_service_odr_cxx11 ${NTP_TIME_SERVICE_ODR_SOURCES}) target_link_libraries(ntp_time_service_odr_cxx11 PRIVATE time_shield::time_shield) set_target_properties(ntp_time_service_odr_cxx11 PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES) +target_compile_options(ntp_time_service_odr_cxx11 PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME ntp_time_service_odr_cxx11 COMMAND ntp_time_service_odr_cxx11) add_executable(ntp_time_service_odr_cxx14 ${NTP_TIME_SERVICE_ODR_SOURCES}) target_link_libraries(ntp_time_service_odr_cxx14 PRIVATE time_shield::time_shield) set_target_properties(ntp_time_service_odr_cxx14 PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED YES) +target_compile_options(ntp_time_service_odr_cxx14 PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME ntp_time_service_odr_cxx14 COMMAND ntp_time_service_odr_cxx14) add_executable(ntp_time_service_odr_cxx17 ${NTP_TIME_SERVICE_ODR_SOURCES}) target_link_libraries(ntp_time_service_odr_cxx17 PRIVATE time_shield::time_shield) set_target_properties(ntp_time_service_odr_cxx17 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +target_compile_options(ntp_time_service_odr_cxx17 PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME ntp_time_service_odr_cxx17 COMMAND ntp_time_service_odr_cxx17) diff --git a/tests/time_conversions_coverage_test.cpp b/tests/time_conversions_coverage_test.cpp index 28114503..68bb2ce3 100644 --- a/tests/time_conversions_coverage_test.cpp +++ b/tests/time_conversions_coverage_test.cpp @@ -5,6 +5,9 @@ #include #include +static_assert(time_shield::MIN_PER_30_DAY == 30 * time_shield::MIN_PER_DAY, + "MIN_PER_30_DAY must represent thirty days"); + int main() { using namespace time_shield; From 5d0205f29f793effd6d52437358a469efcf0086d Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Tue, 8 Sep 2026 22:32:33 +0300 Subject: [PATCH 2/6] docs(review): restore agent execution checklist Document the review, build, documentation, alias, and manual MQL5 validation workflow alongside the repository guard and test policies. --- AGENTS.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 027aed59..7ea83113 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,3 +50,17 @@ Header guard rules: `TIME_SHIELD_MQL5_HEADER____INCLUDED`. - Do not use guard names that start with an underscore, start with an underscore followed by an uppercase letter, or contain a double underscore. + +## Agent execution checklist + +Use the following order for repository changes and reviews: + +1. Inspect repository guidance and the current working tree before editing. +2. Review code, tests, build configuration, public documentation, and release metadata as separate concerns. +3. Build with CMake and the `MinGW Makefiles` generator. Keep build and test directories under `tmp/agent-work/`. +4. Run CTest in both Debug and Release configurations. Test targets must keep assertions enabled in Release builds. +5. Check C++11, C++14, and C++17 compatibility, installation consumers, and ODR tests when public headers change. +6. Treat MQL5 scripts as manual tests unless a MetaEditor compiler is available. Manual scripts report an aggregate pass/fail result. +7. Preserve convenient public aliases. Move an alias to an opt-in legacy header only when its legacy status is confirmed by history and documentation. +8. Keep Doxygen source and generated-site inputs consistent with the project version. Do not commit placeholder versions. +9. Finish with `git diff --check`, a status review, and a concise summary of remaining risks or unverified platform-specific checks. From f272236d21ab31cac71751c94d16d9e97748f0a4 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Tue, 8 Sep 2026 23:02:07 +0300 Subject: [PATCH 3/6] fix(ci): align test checks and docs versioning Remove obsolete NDEBUG overrides now that runtime tests use TIME_SHIELD_TEST_CHECK. Select documentation versions from CMake on main and release tags, and publish documentation for v* tag pushes. --- .github/workflows/publish.yaml | 16 +++++++++++----- AGENTS.md | 2 +- CMakeLists.txt | 5 ----- tests/odr/CMakeLists.txt | 7 ------- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index a467f50c..aca93096 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -4,6 +4,8 @@ on: push: branches: - main + tags: + - "v*" workflow_dispatch: jobs: @@ -17,13 +19,17 @@ jobs: fetch-depth: 0 - name: Inject version into documentation sources run: | - TAG=$(git describe --tags --abbrev=0 2>/dev/null || sed -n 's/^project(TimeShield VERSION \([^ ]*\).*/\1/p' CMakeLists.txt) - test -n "$TAG" || { echo "Unable to determine project version"; exit 1; } - TAG=$(echo "$TAG" | sed 's/^v//') - echo "Using version: $TAG" + if [ "$GITHUB_REF_TYPE" = "tag" ]; then + VERSION="${GITHUB_REF_NAME#v}" + else + VERSION=$(sed -nE 's/^project\(TimeShield VERSION ([^ ]+) .*/\1/p' CMakeLists.txt) + fi + test -n "$VERSION" || { echo "Unable to determine project version"; exit 1; } + echo "Using version: $VERSION" + TAG="$VERSION" test -f docs/mainpage.md || { echo "mainpage.md not found!"; exit 1; } sed -i -E "s/^(\\*\\*Version:\*\\* ).*/\\1\\`$TAG\\`/" docs/mainpage.md - sed -i -E "s/^PROJECT_NUMBER[[:space:]]*=.*/PROJECT_NUMBER = $TAG/" Doxyfile + sed -i -E "s/^PROJECT_NUMBER[[:space:]]*=.*/PROJECT_NUMBER = $VERSION/" Doxyfile - name: Generate Documentation uses: mattnotmitt/doxygen-action@edge - name: Publish generated content to GitHub Pages diff --git a/AGENTS.md b/AGENTS.md index 7ea83113..714e68e5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -58,7 +58,7 @@ Use the following order for repository changes and reviews: 1. Inspect repository guidance and the current working tree before editing. 2. Review code, tests, build configuration, public documentation, and release metadata as separate concerns. 3. Build with CMake and the `MinGW Makefiles` generator. Keep build and test directories under `tmp/agent-work/`. -4. Run CTest in both Debug and Release configurations. Test targets must keep assertions enabled in Release builds. +4. Run CTest in both Debug and Release configurations. Runtime test checks must remain active in Release builds. 5. Check C++11, C++14, and C++17 compatibility, installation consumers, and ODR tests when public headers change. 6. Treat MQL5 scripts as manual tests unless a MetaEditor compiler is available. Manual scripts report an aggregate pass/fail result. 7. Preserve convenient public aliases. Move an alias to an opt-in legacy header only when its legacy status is confirmed by history and documentation. diff --git a/CMakeLists.txt b/CMakeLists.txt index 83e9c698..10f8fb61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,10 +79,8 @@ option(TIME_SHIELD_CPP_BUILD_EXAMPLES "Build examples" ${is_top_level}) option(TIME_SHIELD_CPP_BUILD_TESTS "Build tests" ${is_top_level}) if(MSVC) set(COMMON_WARN_FLAGS /W4 /wd4996) - set(TEST_ASSERT_FLAGS /UNDEBUG) else() set(COMMON_WARN_FLAGS -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wshadow) - set(TEST_ASSERT_FLAGS -UNDEBUG) endif() file(GLOB_RECURSE PROJECT_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} include/*.hpp) @@ -112,9 +110,6 @@ if(TIME_SHIELD_CPP_BUILD_TESTS) if(COMMON_WARN_FLAGS) target_compile_options(${test_name} PRIVATE ${COMMON_WARN_FLAGS}) endif() - if(TEST_ASSERT_FLAGS) - target_compile_options(${test_name} PRIVATE ${TEST_ASSERT_FLAGS}) - endif() add_test(NAME ${test_name} COMMAND ${test_name}) endforeach() diff --git a/tests/odr/CMakeLists.txt b/tests/odr/CMakeLists.txt index cf9182d0..b1625f68 100644 --- a/tests/odr/CMakeLists.txt +++ b/tests/odr/CMakeLists.txt @@ -6,28 +6,24 @@ set(ODR_SOURCES add_executable(odr_cxx11 ${ODR_SOURCES}) target_link_libraries(odr_cxx11 PRIVATE time_shield::time_shield) set_target_properties(odr_cxx11 PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES) -target_compile_options(odr_cxx11 PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME odr_cxx11 COMMAND odr_cxx11) add_executable(cxx11_header_compat cxx11_header_compat.cpp) target_link_libraries(cxx11_header_compat PRIVATE time_shield::time_shield) set_target_properties(cxx11_header_compat PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES) -target_compile_options(cxx11_header_compat PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME cxx11_header_compat COMMAND cxx11_header_compat) add_executable(odr_cxx14 ${ODR_SOURCES}) target_link_libraries(odr_cxx14 PRIVATE time_shield::time_shield) set_target_properties(odr_cxx14 PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED YES) -target_compile_options(odr_cxx14 PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME odr_cxx14 COMMAND odr_cxx14) add_executable(odr_cxx17 ${ODR_SOURCES}) target_link_libraries(odr_cxx17 PRIVATE time_shield::time_shield) set_target_properties(odr_cxx17 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) -target_compile_options(odr_cxx17 PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME odr_cxx17 COMMAND odr_cxx17) @@ -39,20 +35,17 @@ set(NTP_TIME_SERVICE_ODR_SOURCES add_executable(ntp_time_service_odr_cxx11 ${NTP_TIME_SERVICE_ODR_SOURCES}) target_link_libraries(ntp_time_service_odr_cxx11 PRIVATE time_shield::time_shield) set_target_properties(ntp_time_service_odr_cxx11 PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES) -target_compile_options(ntp_time_service_odr_cxx11 PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME ntp_time_service_odr_cxx11 COMMAND ntp_time_service_odr_cxx11) add_executable(ntp_time_service_odr_cxx14 ${NTP_TIME_SERVICE_ODR_SOURCES}) target_link_libraries(ntp_time_service_odr_cxx14 PRIVATE time_shield::time_shield) set_target_properties(ntp_time_service_odr_cxx14 PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED YES) -target_compile_options(ntp_time_service_odr_cxx14 PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME ntp_time_service_odr_cxx14 COMMAND ntp_time_service_odr_cxx14) add_executable(ntp_time_service_odr_cxx17 ${NTP_TIME_SERVICE_ODR_SOURCES}) target_link_libraries(ntp_time_service_odr_cxx17 PRIVATE time_shield::time_shield) set_target_properties(ntp_time_service_odr_cxx17 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) -target_compile_options(ntp_time_service_odr_cxx17 PRIVATE ${TEST_ASSERT_FLAGS}) add_test(NAME ntp_time_service_odr_cxx17 COMMAND ntp_time_service_odr_cxx17) From b82f964e37e10e0dddedd86e4e7916767d3c36ed Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Wed, 9 Sep 2026 20:08:20 +0300 Subject: [PATCH 4/6] fix(ci): correct documentation version substitution Escape the Markdown backticks correctly in the publish workflow and use the computed VERSION value directly. --- .github/workflows/publish.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index aca93096..1ad4f424 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -26,9 +26,8 @@ jobs: fi test -n "$VERSION" || { echo "Unable to determine project version"; exit 1; } echo "Using version: $VERSION" - TAG="$VERSION" test -f docs/mainpage.md || { echo "mainpage.md not found!"; exit 1; } - sed -i -E "s/^(\\*\\*Version:\*\\* ).*/\\1\\`$TAG\\`/" docs/mainpage.md + sed -i -E "s/^(\*\*Version:\*\* ).*/\1\`$VERSION\`/" docs/mainpage.md sed -i -E "s/^PROJECT_NUMBER[[:space:]]*=.*/PROJECT_NUMBER = $VERSION/" Doxyfile - name: Generate Documentation uses: mattnotmitt/doxygen-action@edge From b28843f20b416e35d1e6e166f747bab85b5aba46 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Wed, 9 Sep 2026 20:29:17 +0300 Subject: [PATCH 5/6] fix(docs): restore build-time version templates Keep documentation sources version-neutral and inject the project or release-tag version during publication. Reject release tags that do not match the CMake project version. --- .github/workflows/publish.yaml | 14 ++++++++++---- Doxyfile | 2 +- docs/mainpage.md | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 1ad4f424..14032e77 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -17,17 +17,23 @@ jobs: with: submodules: "true" fetch-depth: 0 - - name: Inject version into documentation sources + - name: Inject documentation version run: | + CMAKE_VERSION=$(sed -nE 's/^project\(TimeShield VERSION ([^ ]+) .*/\1/p' CMakeLists.txt) + test -n "$CMAKE_VERSION" || { echo "Unable to determine CMake project version"; exit 1; } if [ "$GITHUB_REF_TYPE" = "tag" ]; then VERSION="${GITHUB_REF_NAME#v}" + if [ "$VERSION" != "$CMAKE_VERSION" ]; then + echo "Tag version $VERSION does not match CMake project version $CMAKE_VERSION" + exit 1 + fi else - VERSION=$(sed -nE 's/^project\(TimeShield VERSION ([^ ]+) .*/\1/p' CMakeLists.txt) + VERSION="$CMAKE_VERSION" fi test -n "$VERSION" || { echo "Unable to determine project version"; exit 1; } - echo "Using version: $VERSION" + echo "Documentation version: $VERSION" test -f docs/mainpage.md || { echo "mainpage.md not found!"; exit 1; } - sed -i -E "s/^(\*\*Version:\*\* ).*/\1\`$VERSION\`/" docs/mainpage.md + sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" docs/mainpage.md sed -i -E "s/^PROJECT_NUMBER[[:space:]]*=.*/PROJECT_NUMBER = $VERSION/" Doxyfile - name: Generate Documentation uses: mattnotmitt/doxygen-action@edge diff --git a/Doxyfile b/Doxyfile index 8cd3d462..8ce3623f 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = "Time Shield Library" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 1.0.6 +PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/docs/mainpage.md b/docs/mainpage.md index af1bc569..2228bc6b 100644 --- a/docs/mainpage.md +++ b/docs/mainpage.md @@ -1,6 +1,6 @@ \mainpage Time Shield Library -**Version:** 1.0.6 +**Version:** `VERSION_PLACEHOLDER` \section intro_sec Introduction From fc7d583c7cdf036e12f572bf3ce787fe1420d2d5 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Thu, 10 Sep 2026 02:28:40 +0300 Subject: [PATCH 6/6] docs(agents): clarify documentation version templates Allow intentional version-neutral documentation templates and distinguish them from generated substitutions applied during publication. --- AGENTS.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 714e68e5..72976f82 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -62,5 +62,7 @@ Use the following order for repository changes and reviews: 5. Check C++11, C++14, and C++17 compatibility, installation consumers, and ODR tests when public headers change. 6. Treat MQL5 scripts as manual tests unless a MetaEditor compiler is available. Manual scripts report an aggregate pass/fail result. 7. Preserve convenient public aliases. Move an alias to an opt-in legacy header only when its legacy status is confirmed by history and documentation. -8. Keep Doxygen source and generated-site inputs consistent with the project version. Do not commit placeholder versions. +8. Keep documentation source templates version-neutral. Do not commit generated + documentation version substitutions; publish.yaml injects the effective version + during publication. 9. Finish with `git diff --check`, a status review, and a concise summary of remaining risks or unverified platform-specific checks.