Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Не включать эти папки в 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
*.mq5 text diff
*.mqh text diff
168 changes: 168 additions & 0 deletions MQL4/Include/TimeShield.mqh
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
//+------------------------------------------------------------------+
//| TimeShield.mqh |
//| Copyright 2025, NewYaroslav |
//| https://github.com/NewYaroslav/time-shield-cpp |
//+------------------------------------------------------------------+
#ifndef TIME_SHIELD_MQL4_MQH_INCLUDED
#define TIME_SHIELD_MQL4_MQH_INCLUDED

/// \file TimeShield.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 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_INCLUDED
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<img src="docs/logo-1280x640.png" alt="Logo" width="600"/>

![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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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`.

Expand All @@ -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).
Expand Down Expand Up @@ -179,7 +181,15 @@ 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 header:

```mql
#include <TimeShield.mqh>

long now_ms = TimeShield::now();
int second_day = TimeShield::sec_of_day(TimeCurrent());
```

### Integration Notes

Expand Down
Loading