From 214e3af84b33dafccfb13172d3966f6341be5ef7 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Thu, 2 Jul 2026 20:13:43 +0300 Subject: [PATCH 1/4] feat(mql4): add TimeShield facade Add a minimal MQL4 include package with time constants, timestamp helpers, second-of-day conversion, and time-of-day parsing under the time_shield namespace. Document MetaTrader usage and mark MQL sources as text for diffs. --- .gitattributes | 3 + MQL4/Include/TimeShield.mqh | 19 +++++ MQL4/Include/time_shield.mqh | 21 +++++ MQL4/Include/time_shield/constants.mqh | 27 +++++++ MQL4/Include/time_shield/time_conversions.mqh | 35 ++++++++ MQL4/Include/time_shield/time_parser.mqh | 61 ++++++++++++++ MQL4/Include/time_shield/time_utils.mqh | 80 +++++++++++++++++++ README.md | 13 ++- 8 files changed, 255 insertions(+), 4 deletions(-) create mode 100644 MQL4/Include/TimeShield.mqh create mode 100644 MQL4/Include/time_shield.mqh create mode 100644 MQL4/Include/time_shield/constants.mqh create mode 100644 MQL4/Include/time_shield/time_conversions.mqh create mode 100644 MQL4/Include/time_shield/time_parser.mqh create mode 100644 MQL4/Include/time_shield/time_utils.mqh diff --git a/.gitattributes b/.gitattributes index 631303fd..480f6f54 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,6 @@ # Не включать эти папки в GitHub source archive (tar.gz/zip) vcpkg-overlay/ export-ignore .github/ export-ignore +*.mq4 text diff +*.mq5 text diff +*.mqh text diff diff --git a/MQL4/Include/TimeShield.mqh b/MQL4/Include/TimeShield.mqh new file mode 100644 index 00000000..31cba28f --- /dev/null +++ b/MQL4/Include/TimeShield.mqh @@ -0,0 +1,19 @@ +//+------------------------------------------------------------------+ +//| TimeShield.mqh | +//| MQL4 Facade for TimeShield C++ Library | +//| Copyright 2025, NewYaroslav | +//| https://github.com/NewYaroslav/time-shield-cpp | +//+------------------------------------------------------------------+ +#ifndef __TIME_SHIELD_MQL4_FACADE_MQH__ +#define __TIME_SHIELD_MQL4_FACADE_MQH__ + +/// \file TimeShield.mqh +/// \brief MQL4 facade header for the Time Shield library. + +#property copyright "Copyright 2025, NewYaroslav" +#property link "https://github.com/NewYaroslav/time-shield-cpp" +#property strict + +#include + +#endif // __TIME_SHIELD_MQL4_FACADE_MQH__ diff --git a/MQL4/Include/time_shield.mqh b/MQL4/Include/time_shield.mqh new file mode 100644 index 00000000..4e5aed73 --- /dev/null +++ b/MQL4/Include/time_shield.mqh @@ -0,0 +1,21 @@ +//+------------------------------------------------------------------+ +//| time_shield.mqh | +//| Copyright 2025, NewYaroslav | +//| https://github.com/NewYaroslav/time-shield-cpp | +//+------------------------------------------------------------------+ +#ifndef __TIME_SHIELD_MQL4_MQH__ +#define __TIME_SHIELD_MQL4_MQH__ + +/// \file time_shield.mqh +/// \brief Main MQL4 header file for the Time Shield library. + +#property copyright "Copyright 2025, NewYaroslav" +#property link "https://github.com/NewYaroslav/time-shield-cpp" +#property strict + +#include +#include +#include +#include + +#endif // __TIME_SHIELD_MQL4_MQH__ diff --git a/MQL4/Include/time_shield/constants.mqh b/MQL4/Include/time_shield/constants.mqh new file mode 100644 index 00000000..c614bc5e --- /dev/null +++ b/MQL4/Include/time_shield/constants.mqh @@ -0,0 +1,27 @@ +//+------------------------------------------------------------------+ +//| constants.mqh | +//| Time Shield - MQL4 Constants | +//| Copyright 2025, NewYaroslav | +//| https://github.com/NewYaroslav/time-shield-cpp | +//+------------------------------------------------------------------+ +#ifndef __TIME_SHIELD_MQL4_CONSTANTS_MQH__ +#define __TIME_SHIELD_MQL4_CONSTANTS_MQH__ + +/// \file constants.mqh +/// \brief Header file with MQL4 time-related constants. + +#property copyright "Copyright 2025, NewYaroslav" +#property link "https://github.com/NewYaroslav/time-shield-cpp" +#property strict + +namespace time_shield { + + const long MS_PER_SEC = 1000; ///< Milliseconds per second. + const long US_PER_SEC = 1000000; ///< Microseconds per second. + const long SEC_PER_MIN = 60; ///< Seconds per minute. + const long SEC_PER_HOUR = 3600; ///< Seconds per hour. + const long SEC_PER_DAY = 86400; ///< Seconds per day. + +} // namespace time_shield + +#endif // __TIME_SHIELD_MQL4_CONSTANTS_MQH__ diff --git a/MQL4/Include/time_shield/time_conversions.mqh b/MQL4/Include/time_shield/time_conversions.mqh new file mode 100644 index 00000000..31f410d7 --- /dev/null +++ b/MQL4/Include/time_shield/time_conversions.mqh @@ -0,0 +1,35 @@ +//+------------------------------------------------------------------+ +//| time_conversions.mqh | +//| Time Shield - MQL4 Time Conversions | +//| Copyright 2025, NewYaroslav | +//| https://github.com/NewYaroslav/time-shield-cpp | +//+------------------------------------------------------------------+ +#ifndef __TIME_SHIELD_MQL4_TIME_CONVERSIONS_MQH__ +#define __TIME_SHIELD_MQL4_TIME_CONVERSIONS_MQH__ + +/// \file time_conversions.mqh +/// \brief Header with MQL4 time conversion helpers. + +#property copyright "Copyright 2025, NewYaroslav" +#property link "https://github.com/NewYaroslav/time-shield-cpp" +#property strict + +#include + +namespace time_shield { + + int sec_of_day(const long timestamp) { + return (int)(timestamp % SEC_PER_DAY); + } + + int sec_of_day_ms(const long timestamp_ms) { + return sec_of_day(timestamp_ms / MS_PER_SEC); + } + + int sec_of_day(const int hour, const int minute, const int second) { + return hour * (int)SEC_PER_HOUR + minute * (int)SEC_PER_MIN + second; + } + +} // namespace time_shield + +#endif // __TIME_SHIELD_MQL4_TIME_CONVERSIONS_MQH__ diff --git a/MQL4/Include/time_shield/time_parser.mqh b/MQL4/Include/time_shield/time_parser.mqh new file mode 100644 index 00000000..00b9605e --- /dev/null +++ b/MQL4/Include/time_shield/time_parser.mqh @@ -0,0 +1,61 @@ +//+------------------------------------------------------------------+ +//| time_parser.mqh | +//| Time Shield - MQL4 Time Parser | +//| Copyright 2025, NewYaroslav | +//| https://github.com/NewYaroslav/time-shield-cpp | +//+------------------------------------------------------------------+ +#ifndef __TIME_SHIELD_MQL4_TIME_PARSER_MQH__ +#define __TIME_SHIELD_MQL4_TIME_PARSER_MQH__ + +/// \file time_parser.mqh +/// \brief Header with MQL4 time parsing helpers. + +#property copyright "Copyright 2025, NewYaroslav" +#property link "https://github.com/NewYaroslav/time-shield-cpp" +#property strict + +#include +#include + +namespace time_shield { + + bool is_valid_time(const int hour, const int minute, const int second) { + return hour >= 0 && hour < 24 + && minute >= 0 && minute < 60 + && second >= 0 && second < 60; + } + + int sec_of_day(const string value) { + string parts[]; + const ushort separator = StringGetCharacter(":", 0); + const int count = StringSplit(value, separator, parts); + + if(count < 1 || count > 3) { + ArrayFree(parts); + return (int)SEC_PER_DAY; + } + + int hour = 0; + int minute = 0; + int second = 0; + + hour = (int)StringToInteger(parts[0]); + if(count > 1) { + minute = (int)StringToInteger(parts[1]); + } + if(count > 2) { + second = (int)StringToInteger(parts[2]); + } + + ArrayFree(parts); + + if(!is_valid_time(hour, minute, second)) { + return (int)SEC_PER_DAY; + } + + return sec_of_day(hour, minute, second); + } + +} // namespace time_shield + +#endif // __TIME_SHIELD_MQL4_TIME_PARSER_MQH__ diff --git a/MQL4/Include/time_shield/time_utils.mqh b/MQL4/Include/time_shield/time_utils.mqh new file mode 100644 index 00000000..278af515 --- /dev/null +++ b/MQL4/Include/time_shield/time_utils.mqh @@ -0,0 +1,80 @@ +//+------------------------------------------------------------------+ +//| time_utils.mqh | +//| Time Shield - MQL4 Time Utilities | +//| Copyright 2025, NewYaroslav | +//| https://github.com/NewYaroslav/time-shield-cpp | +//+------------------------------------------------------------------+ +#ifndef __TIME_SHIELD_MQL4_TIME_UTILS_MQH__ +#define __TIME_SHIELD_MQL4_TIME_UTILS_MQH__ + +/// \file time_utils.mqh +/// \brief Header with MQL4 time-related utility functions. + +#property copyright "Copyright 2025, NewYaroslav" +#property link "https://github.com/NewYaroslav/time-shield-cpp" +#property strict + +#include + +namespace time_shield { + + long tick_count_ms() { + return (long)GetTickCount(); + } + + long monotonic_ms() { + static bool initialized = false; + static ulong last_raw = 0; + static long high = 0; + + const ulong raw = (ulong)GetTickCount(); + if(!initialized) { + initialized = true; + last_raw = raw; + return (long)raw; + } + + if(raw < last_raw) { + high += 4294967296; + } + last_raw = raw; + + return high + (long)raw; + } + + long ts() { + return (long)TimeGMT(); + } + + long timestamp() { + return ts(); + } + + long ts_ms() { + static bool initialized = false; + static long offset = 0; + + if(!initialized) { + const long start_ts = ts(); + long next_ts = start_ts; + while((next_ts = ts()) == start_ts) { + } + + offset = next_ts * MS_PER_SEC - monotonic_ms(); + initialized = true; + } + + return monotonic_ms() + offset; + } + + long timestamp_ms() { + return ts_ms(); + } + + long now() { + return ts_ms(); + } + +} // namespace time_shield + +#endif // __TIME_SHIELD_MQL4_TIME_UTILS_MQH__ diff --git a/README.md b/README.md index d583b7d5..b40c9d92 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Logo ![MIT License](https://img.shields.io/badge/license-MIT-green.svg) -![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS%20%7C%20MQL5-blue) +![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS%20%7C%20MQL4%20%7C%20MQL5-blue) ![C++ Standard](https://img.shields.io/badge/C++-11--17-orange) ![CI Windows](https://img.shields.io/github/actions/workflow/status/newyaroslav/time-shield-cpp/ci.yml?branch=main&label=Windows&logo=windows) ![CI Linux](https://img.shields.io/github/actions/workflow/status/newyaroslav/time-shield-cpp/ci.yml?branch=main&label=Linux&logo=linux) @@ -47,7 +47,7 @@ more academic solutions like `HowardHinnant/date`, the library: parts of a timestamp and calculating period boundaries; - has an **extensible architecture**—new formats (Julian, OLE, UTC offset) can be added as separate types and modules; -- **works even in restricted environments** such as MQL5/MetaTrader—no +- **works even in restricted environments** such as MQL4/MQL5/MetaTrader—no exceptions or dynamic memory are required for the core API; optional helpers can use `std::string` or throw and can be disabled in restricted builds; - ships as **header-only**—a single include without build steps or external @@ -76,7 +76,7 @@ more academic solutions like `HowardHinnant/date`, the library: works with fractions of a second. - **Time zone conversion**—functions for European, US, and selected Asia/EMEA trading zones plus generic zone-to-zone conversion. - **NTP client and pool**—single-client queries plus a configurable pool/runner/service pipeline with offline testing hooks (Windows and Unix). -- **MQL5 support**—adapted headers in the `MQL5` directory allow using the +- **MQL4/MQL5 support**—adapted headers in the `MQL4` and `MQL5` directories allow using the library in MetaTrader. - Compatible with `C++11`–`C++17`. @@ -179,7 +179,12 @@ Examples can be built with the provided scripts: - `build_examples.sh` for Linux/macOS; - `build-cb.bat` to generate a Code::Blocks project. -Use `install_mql5.bat` to install the MQL5 files. +Use `install_mql5.bat` to install the MQL5 files. For MQL4, copy +`MQL4/Include` into the terminal `Include` directory and include the facade: + +```mql +#include +``` ### Integration Notes From 458e897df58eff29e064360013bdd169a79808ad Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Thu, 2 Jul 2026 20:31:14 +0300 Subject: [PATCH 2/4] fix(mql4): expose TimeShield class API Replace the MQL4 namespace-style headers with a static TimeShield class facade, matching MQL4 language constraints while preserving the TimeShield.mqh entry point. --- MQL4/Include/time_shield.mqh | 155 +++++++++++++++++- MQL4/Include/time_shield/constants.mqh | 27 --- MQL4/Include/time_shield/time_conversions.mqh | 35 ---- MQL4/Include/time_shield/time_parser.mqh | 61 ------- MQL4/Include/time_shield/time_utils.mqh | 80 --------- README.md | 9 +- 6 files changed, 158 insertions(+), 209 deletions(-) delete mode 100644 MQL4/Include/time_shield/constants.mqh delete mode 100644 MQL4/Include/time_shield/time_conversions.mqh delete mode 100644 MQL4/Include/time_shield/time_parser.mqh delete mode 100644 MQL4/Include/time_shield/time_utils.mqh diff --git a/MQL4/Include/time_shield.mqh b/MQL4/Include/time_shield.mqh index 4e5aed73..55973a0d 100644 --- a/MQL4/Include/time_shield.mqh +++ b/MQL4/Include/time_shield.mqh @@ -13,9 +13,156 @@ #property link "https://github.com/NewYaroslav/time-shield-cpp" #property strict -#include -#include -#include -#include +/// \class TimeShield +/// \brief Provides namespace-style static time helpers for MQL4. +class TimeShield { +public: + enum Constants { + MS_PER_SEC = 1000, ///< Milliseconds per second. + US_PER_SEC = 1000000, ///< Microseconds per second. + SEC_PER_MIN = 60, ///< Seconds per minute. + SEC_PER_HOUR = 3600, ///< Seconds per hour. + SEC_PER_DAY = 86400 ///< Seconds per day. + }; + + /// \brief Get raw terminal tick counter in milliseconds. + /// \return Wrapped 32-bit terminal tick counter. + static uint tick_count_ms() { + return GetTickCount(); + } + + /// \brief Get monotonic milliseconds since terminal start with wrap handling. + /// \return Monotonic millisecond counter. + static ulong monotonic_ms() { + static bool initialized = false; + static uint last_raw = 0; + static ulong high = 0; + + const uint raw = GetTickCount(); + if(!initialized) { + initialized = true; + last_raw = raw; + return (ulong)raw; + } + + if(raw < last_raw) { + high += 4294967296; + } + last_raw = raw; + + return high + raw; + } + + /// \brief Get current UTC timestamp in seconds. + /// \return Current UTC timestamp in seconds. + static long ts() { + return (long)TimeGMT(); + } + + /// \brief Alias for ts(). + /// \return Current UTC timestamp in seconds. + static long timestamp() { + return ts(); + } + + /// \brief Get current UTC timestamp in milliseconds. + /// \return Current UTC timestamp in milliseconds. + static long ts_ms() { + static bool initialized = false; + static long offset = 0; + + if(!initialized) { + const long start_ts = ts(); + long next_ts = start_ts; + while((next_ts = ts()) == start_ts) { + } + + offset = next_ts * MS_PER_SEC - (long)monotonic_ms(); + initialized = true; + } + + return (long)monotonic_ms() + offset; + } + + /// \brief Alias for ts_ms(). + /// \return Current UTC timestamp in milliseconds. + static long timestamp_ms() { + return ts_ms(); + } + + /// \brief Alias for ts_ms(). + /// \return Current UTC timestamp in milliseconds. + static long now() { + return ts_ms(); + } + + /// \brief Get second of day from timestamp. + /// \param timestamp Timestamp in seconds. + /// \return Second of day. + static int sec_of_day(const long timestamp) { + return (int)(timestamp % SEC_PER_DAY); + } + + /// \brief Get second of day from milliseconds timestamp. + /// \param timestamp_ms Timestamp in milliseconds. + /// \return Second of day. + static int sec_of_day_ms(const long timestamp_ms) { + return sec_of_day(timestamp_ms / MS_PER_SEC); + } + + /// \brief Get second of day from hours, minutes and seconds. + /// \param hour Hour value. + /// \param minute Minute value. + /// \param second Second value. + /// \return Second of day. + static int sec_of_day(const int hour, const int minute, const int second) { + return hour * SEC_PER_HOUR + minute * SEC_PER_MIN + second; + } + + /// \brief Check time-of-day components. + /// \param hour Hour value. + /// \param minute Minute value. + /// \param second Second value. + /// \return True when the time is valid. + static bool is_valid_time(const int hour, const int minute, const int second) { + return hour >= 0 && hour < 24 + && minute >= 0 && minute < 60 + && second >= 0 && second < 60; + } + + /// \brief Convert string with time of day to second of day. + /// \param value Time string in HH, HH:MM, or HH:MM:SS format. + /// \return Second of day or SEC_PER_DAY on parse failure. + static int sec_of_day(const string value) { + string parts[]; + const ushort separator = StringGetCharacter(":", 0); + const int count = StringSplit(value, separator, parts); + + if(count < 1 || count > 3) { + ArrayFree(parts); + return SEC_PER_DAY; + } + + int hour = 0; + int minute = 0; + int second = 0; + + hour = (int)StringToInteger(parts[0]); + if(count > 1) { + minute = (int)StringToInteger(parts[1]); + } + if(count > 2) { + second = (int)StringToInteger(parts[2]); + } + + ArrayFree(parts); + + if(!is_valid_time(hour, minute, second)) { + return SEC_PER_DAY; + } + + return sec_of_day(hour, minute, second); + } +}; #endif // __TIME_SHIELD_MQL4_MQH__ diff --git a/MQL4/Include/time_shield/constants.mqh b/MQL4/Include/time_shield/constants.mqh deleted file mode 100644 index c614bc5e..00000000 --- a/MQL4/Include/time_shield/constants.mqh +++ /dev/null @@ -1,27 +0,0 @@ -//+------------------------------------------------------------------+ -//| constants.mqh | -//| Time Shield - MQL4 Constants | -//| Copyright 2025, NewYaroslav | -//| https://github.com/NewYaroslav/time-shield-cpp | -//+------------------------------------------------------------------+ -#ifndef __TIME_SHIELD_MQL4_CONSTANTS_MQH__ -#define __TIME_SHIELD_MQL4_CONSTANTS_MQH__ - -/// \file constants.mqh -/// \brief Header file with MQL4 time-related constants. - -#property copyright "Copyright 2025, NewYaroslav" -#property link "https://github.com/NewYaroslav/time-shield-cpp" -#property strict - -namespace time_shield { - - const long MS_PER_SEC = 1000; ///< Milliseconds per second. - const long US_PER_SEC = 1000000; ///< Microseconds per second. - const long SEC_PER_MIN = 60; ///< Seconds per minute. - const long SEC_PER_HOUR = 3600; ///< Seconds per hour. - const long SEC_PER_DAY = 86400; ///< Seconds per day. - -} // namespace time_shield - -#endif // __TIME_SHIELD_MQL4_CONSTANTS_MQH__ diff --git a/MQL4/Include/time_shield/time_conversions.mqh b/MQL4/Include/time_shield/time_conversions.mqh deleted file mode 100644 index 31f410d7..00000000 --- a/MQL4/Include/time_shield/time_conversions.mqh +++ /dev/null @@ -1,35 +0,0 @@ -//+------------------------------------------------------------------+ -//| time_conversions.mqh | -//| Time Shield - MQL4 Time Conversions | -//| Copyright 2025, NewYaroslav | -//| https://github.com/NewYaroslav/time-shield-cpp | -//+------------------------------------------------------------------+ -#ifndef __TIME_SHIELD_MQL4_TIME_CONVERSIONS_MQH__ -#define __TIME_SHIELD_MQL4_TIME_CONVERSIONS_MQH__ - -/// \file time_conversions.mqh -/// \brief Header with MQL4 time conversion helpers. - -#property copyright "Copyright 2025, NewYaroslav" -#property link "https://github.com/NewYaroslav/time-shield-cpp" -#property strict - -#include - -namespace time_shield { - - int sec_of_day(const long timestamp) { - return (int)(timestamp % SEC_PER_DAY); - } - - int sec_of_day_ms(const long timestamp_ms) { - return sec_of_day(timestamp_ms / MS_PER_SEC); - } - - int sec_of_day(const int hour, const int minute, const int second) { - return hour * (int)SEC_PER_HOUR + minute * (int)SEC_PER_MIN + second; - } - -} // namespace time_shield - -#endif // __TIME_SHIELD_MQL4_TIME_CONVERSIONS_MQH__ diff --git a/MQL4/Include/time_shield/time_parser.mqh b/MQL4/Include/time_shield/time_parser.mqh deleted file mode 100644 index 00b9605e..00000000 --- a/MQL4/Include/time_shield/time_parser.mqh +++ /dev/null @@ -1,61 +0,0 @@ -//+------------------------------------------------------------------+ -//| time_parser.mqh | -//| Time Shield - MQL4 Time Parser | -//| Copyright 2025, NewYaroslav | -//| https://github.com/NewYaroslav/time-shield-cpp | -//+------------------------------------------------------------------+ -#ifndef __TIME_SHIELD_MQL4_TIME_PARSER_MQH__ -#define __TIME_SHIELD_MQL4_TIME_PARSER_MQH__ - -/// \file time_parser.mqh -/// \brief Header with MQL4 time parsing helpers. - -#property copyright "Copyright 2025, NewYaroslav" -#property link "https://github.com/NewYaroslav/time-shield-cpp" -#property strict - -#include -#include - -namespace time_shield { - - bool is_valid_time(const int hour, const int minute, const int second) { - return hour >= 0 && hour < 24 - && minute >= 0 && minute < 60 - && second >= 0 && second < 60; - } - - int sec_of_day(const string value) { - string parts[]; - const ushort separator = StringGetCharacter(":", 0); - const int count = StringSplit(value, separator, parts); - - if(count < 1 || count > 3) { - ArrayFree(parts); - return (int)SEC_PER_DAY; - } - - int hour = 0; - int minute = 0; - int second = 0; - - hour = (int)StringToInteger(parts[0]); - if(count > 1) { - minute = (int)StringToInteger(parts[1]); - } - if(count > 2) { - second = (int)StringToInteger(parts[2]); - } - - ArrayFree(parts); - - if(!is_valid_time(hour, minute, second)) { - return (int)SEC_PER_DAY; - } - - return sec_of_day(hour, minute, second); - } - -} // namespace time_shield - -#endif // __TIME_SHIELD_MQL4_TIME_PARSER_MQH__ diff --git a/MQL4/Include/time_shield/time_utils.mqh b/MQL4/Include/time_shield/time_utils.mqh deleted file mode 100644 index 278af515..00000000 --- a/MQL4/Include/time_shield/time_utils.mqh +++ /dev/null @@ -1,80 +0,0 @@ -//+------------------------------------------------------------------+ -//| time_utils.mqh | -//| Time Shield - MQL4 Time Utilities | -//| Copyright 2025, NewYaroslav | -//| https://github.com/NewYaroslav/time-shield-cpp | -//+------------------------------------------------------------------+ -#ifndef __TIME_SHIELD_MQL4_TIME_UTILS_MQH__ -#define __TIME_SHIELD_MQL4_TIME_UTILS_MQH__ - -/// \file time_utils.mqh -/// \brief Header with MQL4 time-related utility functions. - -#property copyright "Copyright 2025, NewYaroslav" -#property link "https://github.com/NewYaroslav/time-shield-cpp" -#property strict - -#include - -namespace time_shield { - - long tick_count_ms() { - return (long)GetTickCount(); - } - - long monotonic_ms() { - static bool initialized = false; - static ulong last_raw = 0; - static long high = 0; - - const ulong raw = (ulong)GetTickCount(); - if(!initialized) { - initialized = true; - last_raw = raw; - return (long)raw; - } - - if(raw < last_raw) { - high += 4294967296; - } - last_raw = raw; - - return high + (long)raw; - } - - long ts() { - return (long)TimeGMT(); - } - - long timestamp() { - return ts(); - } - - long ts_ms() { - static bool initialized = false; - static long offset = 0; - - if(!initialized) { - const long start_ts = ts(); - long next_ts = start_ts; - while((next_ts = ts()) == start_ts) { - } - - offset = next_ts * MS_PER_SEC - monotonic_ms(); - initialized = true; - } - - return monotonic_ms() + offset; - } - - long timestamp_ms() { - return ts_ms(); - } - - long now() { - return ts_ms(); - } - -} // namespace time_shield - -#endif // __TIME_SHIELD_MQL4_TIME_UTILS_MQH__ diff --git a/README.md b/README.md index b40c9d92..96740743 100644 --- a/README.md +++ b/README.md @@ -92,8 +92,10 @@ library and report platform capabilities: - `TIME_SHIELD_ENABLE_NTP_CLIENT` — enables the optional `NtpClient` module (defaults to `1` on supported platforms). -All public headers place their declarations inside the `time_shield` namespace. -Use `time_shield::` or `using namespace time_shield;` to access the API. +C++ and MQL5 public headers place their declarations inside the `time_shield` +namespace. Use `time_shield::` or `using namespace time_shield;` to access that +API. MQL4 uses the `TimeShield` class with static methods and constants because +the MQL4 language does not provide namespaces. > Some functions depend on platform APIs and may be limited (for example, > obtaining realtime via `QueryPerformanceCounter` on Windows). @@ -184,6 +186,9 @@ Use `install_mql5.bat` to install the MQL5 files. For MQL4, copy ```mql #include + +long now_ms = TimeShield::now(); +int second_day = TimeShield::sec_of_day(TimeCurrent()); ``` ### Integration Notes From 9a304da066039694b0b4ff5d8db4e6ff2e7a8f23 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Thu, 2 Jul 2026 20:45:35 +0300 Subject: [PATCH 3/4] fix(mql4): keep single TimeShield header Move the MQL4 static TimeShield API into the public TimeShield.mqh header and remove the duplicate time_shield.mqh entry point. --- MQL4/Include/TimeShield.mqh | 161 +++++++++++++++++++++++++++++++-- MQL4/Include/time_shield.mqh | 168 ----------------------------------- README.md | 2 +- 3 files changed, 156 insertions(+), 175 deletions(-) delete mode 100644 MQL4/Include/time_shield.mqh diff --git a/MQL4/Include/TimeShield.mqh b/MQL4/Include/TimeShield.mqh index 31cba28f..d4e789b1 100644 --- a/MQL4/Include/TimeShield.mqh +++ b/MQL4/Include/TimeShield.mqh @@ -1,19 +1,168 @@ //+------------------------------------------------------------------+ //| TimeShield.mqh | -//| MQL4 Facade for TimeShield C++ Library | //| Copyright 2025, NewYaroslav | //| https://github.com/NewYaroslav/time-shield-cpp | //+------------------------------------------------------------------+ -#ifndef __TIME_SHIELD_MQL4_FACADE_MQH__ -#define __TIME_SHIELD_MQL4_FACADE_MQH__ +#ifndef TIME_SHIELD_MQL4_MQH_INCLUDED +#define TIME_SHIELD_MQL4_MQH_INCLUDED /// \file TimeShield.mqh -/// \brief MQL4 facade header for the Time Shield library. +/// \brief Main MQL4 header file for the Time Shield library. #property copyright "Copyright 2025, NewYaroslav" #property link "https://github.com/NewYaroslav/time-shield-cpp" #property strict -#include +/// \class TimeShield +/// \brief Provides static time helpers for MQL4. +class TimeShield { +public: + enum Constants { + MS_PER_SEC = 1000, ///< Milliseconds per second. + US_PER_SEC = 1000000, ///< Microseconds per second. + SEC_PER_MIN = 60, ///< Seconds per minute. + SEC_PER_HOUR = 3600, ///< Seconds per hour. + SEC_PER_DAY = 86400 ///< Seconds per day. + }; -#endif // __TIME_SHIELD_MQL4_FACADE_MQH__ + /// \brief Get raw terminal tick counter in milliseconds. + /// \return Wrapped 32-bit terminal tick counter. + static uint tick_count_ms() { + return GetTickCount(); + } + + /// \brief Get monotonic milliseconds since terminal start with wrap handling. + /// \return Monotonic millisecond counter. + static ulong monotonic_ms() { + static bool initialized = false; + static uint last_raw = 0; + static ulong high = 0; + + const uint raw = GetTickCount(); + if(!initialized) { + initialized = true; + last_raw = raw; + return (ulong)raw; + } + + if(raw < last_raw) { + high += 4294967296; + } + last_raw = raw; + + return high + raw; + } + + /// \brief Get current UTC timestamp in seconds. + /// \return Current UTC timestamp in seconds. + static long ts() { + return (long)TimeGMT(); + } + + /// \brief Alias for ts(). + /// \return Current UTC timestamp in seconds. + static long timestamp() { + return ts(); + } + + /// \brief Get current UTC timestamp in milliseconds. + /// \return Current UTC timestamp in milliseconds. + static long ts_ms() { + static bool initialized = false; + static long offset = 0; + + if(!initialized) { + const long start_ts = ts(); + long next_ts = start_ts; + while((next_ts = ts()) == start_ts) { + } + + offset = next_ts * MS_PER_SEC - (long)monotonic_ms(); + initialized = true; + } + + return (long)monotonic_ms() + offset; + } + + /// \brief Alias for ts_ms(). + /// \return Current UTC timestamp in milliseconds. + static long timestamp_ms() { + return ts_ms(); + } + + /// \brief Alias for ts_ms(). + /// \return Current UTC timestamp in milliseconds. + static long now() { + return ts_ms(); + } + + /// \brief Get second of day from timestamp. + /// \param timestamp Timestamp in seconds. + /// \return Second of day. + static int sec_of_day(const long timestamp) { + return (int)(timestamp % SEC_PER_DAY); + } + + /// \brief Get second of day from milliseconds timestamp. + /// \param timestamp_ms Timestamp in milliseconds. + /// \return Second of day. + static int sec_of_day_ms(const long timestamp_ms) { + return sec_of_day(timestamp_ms / MS_PER_SEC); + } + + /// \brief Get second of day from hours, minutes and seconds. + /// \param hour Hour value. + /// \param minute Minute value. + /// \param second Second value. + /// \return Second of day. + static int sec_of_day(const int hour, const int minute, const int second) { + return hour * SEC_PER_HOUR + minute * SEC_PER_MIN + second; + } + + /// \brief Check time-of-day components. + /// \param hour Hour value. + /// \param minute Minute value. + /// \param second Second value. + /// \return True when the time is valid. + static bool is_valid_time(const int hour, const int minute, const int second) { + return hour >= 0 && hour < 24 + && minute >= 0 && minute < 60 + && second >= 0 && second < 60; + } + + /// \brief Convert string with time of day to second of day. + /// \param value Time string in HH, HH:MM, or HH:MM:SS format. + /// \return Second of day or SEC_PER_DAY on parse failure. + static int sec_of_day(const string value) { + string parts[]; + const ushort separator = StringGetCharacter(":", 0); + const int count = StringSplit(value, separator, parts); + + if(count < 1 || count > 3) { + ArrayFree(parts); + return SEC_PER_DAY; + } + + int hour = 0; + int minute = 0; + int second = 0; + + hour = (int)StringToInteger(parts[0]); + if(count > 1) { + minute = (int)StringToInteger(parts[1]); + } + if(count > 2) { + second = (int)StringToInteger(parts[2]); + } + + ArrayFree(parts); + + if(!is_valid_time(hour, minute, second)) { + return SEC_PER_DAY; + } + + return sec_of_day(hour, minute, second); + } +}; + +#endif // TIME_SHIELD_MQL4_MQH_INCLUDED diff --git a/MQL4/Include/time_shield.mqh b/MQL4/Include/time_shield.mqh deleted file mode 100644 index 55973a0d..00000000 --- a/MQL4/Include/time_shield.mqh +++ /dev/null @@ -1,168 +0,0 @@ -//+------------------------------------------------------------------+ -//| time_shield.mqh | -//| Copyright 2025, NewYaroslav | -//| https://github.com/NewYaroslav/time-shield-cpp | -//+------------------------------------------------------------------+ -#ifndef __TIME_SHIELD_MQL4_MQH__ -#define __TIME_SHIELD_MQL4_MQH__ - -/// \file time_shield.mqh -/// \brief Main MQL4 header file for the Time Shield library. - -#property copyright "Copyright 2025, NewYaroslav" -#property link "https://github.com/NewYaroslav/time-shield-cpp" -#property strict - -/// \class TimeShield -/// \brief Provides namespace-style static time helpers for MQL4. -class TimeShield { -public: - enum Constants { - MS_PER_SEC = 1000, ///< Milliseconds per second. - US_PER_SEC = 1000000, ///< Microseconds per second. - SEC_PER_MIN = 60, ///< Seconds per minute. - SEC_PER_HOUR = 3600, ///< Seconds per hour. - SEC_PER_DAY = 86400 ///< Seconds per day. - }; - - /// \brief Get raw terminal tick counter in milliseconds. - /// \return Wrapped 32-bit terminal tick counter. - static uint tick_count_ms() { - return GetTickCount(); - } - - /// \brief Get monotonic milliseconds since terminal start with wrap handling. - /// \return Monotonic millisecond counter. - static ulong monotonic_ms() { - static bool initialized = false; - static uint last_raw = 0; - static ulong high = 0; - - const uint raw = GetTickCount(); - if(!initialized) { - initialized = true; - last_raw = raw; - return (ulong)raw; - } - - if(raw < last_raw) { - high += 4294967296; - } - last_raw = raw; - - return high + raw; - } - - /// \brief Get current UTC timestamp in seconds. - /// \return Current UTC timestamp in seconds. - static long ts() { - return (long)TimeGMT(); - } - - /// \brief Alias for ts(). - /// \return Current UTC timestamp in seconds. - static long timestamp() { - return ts(); - } - - /// \brief Get current UTC timestamp in milliseconds. - /// \return Current UTC timestamp in milliseconds. - static long ts_ms() { - static bool initialized = false; - static long offset = 0; - - if(!initialized) { - const long start_ts = ts(); - long next_ts = start_ts; - while((next_ts = ts()) == start_ts) { - } - - offset = next_ts * MS_PER_SEC - (long)monotonic_ms(); - initialized = true; - } - - return (long)monotonic_ms() + offset; - } - - /// \brief Alias for ts_ms(). - /// \return Current UTC timestamp in milliseconds. - static long timestamp_ms() { - return ts_ms(); - } - - /// \brief Alias for ts_ms(). - /// \return Current UTC timestamp in milliseconds. - static long now() { - return ts_ms(); - } - - /// \brief Get second of day from timestamp. - /// \param timestamp Timestamp in seconds. - /// \return Second of day. - static int sec_of_day(const long timestamp) { - return (int)(timestamp % SEC_PER_DAY); - } - - /// \brief Get second of day from milliseconds timestamp. - /// \param timestamp_ms Timestamp in milliseconds. - /// \return Second of day. - static int sec_of_day_ms(const long timestamp_ms) { - return sec_of_day(timestamp_ms / MS_PER_SEC); - } - - /// \brief Get second of day from hours, minutes and seconds. - /// \param hour Hour value. - /// \param minute Minute value. - /// \param second Second value. - /// \return Second of day. - static int sec_of_day(const int hour, const int minute, const int second) { - return hour * SEC_PER_HOUR + minute * SEC_PER_MIN + second; - } - - /// \brief Check time-of-day components. - /// \param hour Hour value. - /// \param minute Minute value. - /// \param second Second value. - /// \return True when the time is valid. - static bool is_valid_time(const int hour, const int minute, const int second) { - return hour >= 0 && hour < 24 - && minute >= 0 && minute < 60 - && second >= 0 && second < 60; - } - - /// \brief Convert string with time of day to second of day. - /// \param value Time string in HH, HH:MM, or HH:MM:SS format. - /// \return Second of day or SEC_PER_DAY on parse failure. - static int sec_of_day(const string value) { - string parts[]; - const ushort separator = StringGetCharacter(":", 0); - const int count = StringSplit(value, separator, parts); - - if(count < 1 || count > 3) { - ArrayFree(parts); - return SEC_PER_DAY; - } - - int hour = 0; - int minute = 0; - int second = 0; - - hour = (int)StringToInteger(parts[0]); - if(count > 1) { - minute = (int)StringToInteger(parts[1]); - } - if(count > 2) { - second = (int)StringToInteger(parts[2]); - } - - ArrayFree(parts); - - if(!is_valid_time(hour, minute, second)) { - return SEC_PER_DAY; - } - - return sec_of_day(hour, minute, second); - } -}; - -#endif // __TIME_SHIELD_MQL4_MQH__ diff --git a/README.md b/README.md index 96740743..30cb3d2b 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ Examples can be built with the provided scripts: - `build-cb.bat` to generate a Code::Blocks project. Use `install_mql5.bat` to install the MQL5 files. For MQL4, copy -`MQL4/Include` into the terminal `Include` directory and include the facade: +`MQL4/Include` into the terminal `Include` directory and include the header: ```mql #include From 8f0f964076070e814fa364a9cfadf388a9c23295 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Thu, 2 Jul 2026 21:04:40 +0300 Subject: [PATCH 4/4] chore: normalize gitattributes comment Replace the mojibake-prone source archive comment with an ASCII English comment while keeping the archive and MQL text attributes unchanged. --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 480f6f54..36171689 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ -# Не включать эти папки в GitHub source archive (tar.gz/zip) +# Exclude these directories from GitHub source archives (tar.gz/zip). vcpkg-overlay/ export-ignore .github/ export-ignore *.mq4 text diff