Skip to content

Latest commit

 

History

History
376 lines (277 loc) · 23 KB

File metadata and controls

376 lines (277 loc) · 23 KB

Time Shield

Логотип

MIT License Platform C++ Standard CI Windows CI Linux CI macOS

Time Shield — это header-only C++-библиотека для работы со временем. Она включает функции для конвертации временных значений, форматирования дат и множество утилит для задач с таймстампами.

Названа в честь «временного щита» Хомуры Акэми.

Быстрый старт

#include <time_shield.hpp>

using namespace time_shield;

ts_t now = ts();
std::string iso = to_iso8601(now);
ts_t gmt = cet_to_gmt(now);
bool monday = is_workday(now);

Используйте #include <time_shield.hpp> для полного API или подключайте отдельные заголовки для минимальной сборки.

Зачем Time Shield?

Time Shield создавался как практичный инструмент для работы с временем в C++, ориентированный на прикладные и инженерные задачи. В отличие от стандартной std::chrono или более академичных решений вроде HowardHinnant/date, библиотека:

  • использует простые типы (int64_t, double) для представления времени (ts_t, fts_t) — их легко логировать, сериализовать и передавать через JSON, RPC и базы данных; без перегруженных классов std::chrono;
  • поддерживает разные временные представления — Unix‑время, дробные секунды, милли- и микросекунды, OLE Automation (Excel), джулианские даты;
  • включает утилиты для округления, форматирования, парсинга ISO 8601, работы с частями времени и вычисления границ периодов;
  • имеет расширяемую архитектуру — новые форматы (Julian, OLE, UTC-offset) добавляются как отдельные типы и модули;
  • есть адаптированные заголовки для MQL5/MetaTrader в каталоге MQL5, покрывающие ключевые части API;
  • поставляется как header-only — подключается одной строкой, без сборки и внешних зависимостей;
  • использует только стандартные заголовки STL и системные API; платформенные модули (например, сокетный NtpClient) изолированы и не мешают кроссплатформенной сборке.

Возможности

  • Календарные и датовые хелперы — валидация, будни/выходные/рабочие дни (включая строки ISO 8601).
  • Форматирование времени — преобразование таймстампов в строки по стандартным и пользовательским шаблонам.
  • Конвертации — перевод между секундными, миллисекундными и плавающими представлениями времени, структуры DateTimeStruct, OLE Automation (Excel) и временные зоны.
  • Быстрые конвертации дат — часть функций timestamp -> календарь использует ускоренный алгоритм, вдохновлённый https://www.benjoffe.com/fast-date-64 и реализованный с нуля.
  • Тип DateTime — обёртка, хранящая UTC миллисекунды и фиксированное смещение, поддерживает ISO 8601, локальные/UTC компоненты и арифметику.
  • ISO week date — конвертация, форматирование и парсинг ISO 8601 для недельного счёта.
  • Астрономические утилиты — расчёт Julian Date/MJD/JDN и оценка лунной фазы/возраста по Unix‑времени.
  • Утилиты — получение текущих меток времени, вычисления начала/конца периодов, работа с частями секунды.
  • Преобразование часовых поясов — функции для европейских, американских и популярных Asia/EMEA trading-зон, а также универсальная zone-to-zone конвертация.
  • NTP‑клиент и пул — одиночные запросы и конфигурируемый пул/раннер/сервис с возможностью офлайн‑тестов (Windows и Unix).
  • Поддержка MQL5 — адаптированные заголовки в каталоге MQL5 позволяют использовать библиотеку в MetaTrader.
  • Совместимость с C++11C++17.

Конфигурация

Компиляционные флаги в time_shield/config.hpp позволяют адаптировать библиотеку под платформу и отключать необязательные модули:

  • TIME_SHIELD_PLATFORM_WINDOWS / TIME_SHIELD_PLATFORM_UNIX — определение целевой платформы.
  • TIME_SHIELD_HAS_WINSOCK — наличие WinSock API.
  • TIME_SHIELD_ENABLE_NTP_CLIENT — включает модуль NtpClient (по умолчанию 1 на поддерживаемых платформах).

Все заголовки библиотеки используют пространство имён time_shield. Для доступа к API можно писать time_shield:: или подключать using namespace time_shield;.

Часть функций зависит от системных API и может быть ограничена платформой (например, получение realtime через QueryPerformanceCounter под Windows).

Инварианты API

  • ts_t — Unix-время в секундах (signed 64-bit). Представляет целые секунды.
  • ts_ms_t / ts_us_t — Unix-время в милли/микросекундах (signed 64-bit).
  • monotonic_sec() / monotonic_ms() / monotonic_us() — монотонные process-local счётчики для интервалов и дедлайнов, а не UTC timestamps.
  • fts_t — Unix-время в секундах как double. Точность дробной части зависит от величины значения; около современной эпохи обычно сохраняет микросекунды, на очень больших |ts| младшие разряды могут теряться.
  • year_t — signed 64-bit год.
  • dse_t / unix_day_t / unixday_t — число суток с 1970-01-01 (signed 64-bit), подходит для дат до эпохи.
  • ISO 8601 утилиты используют пролептический григорианский календарь и не учитывают високосные секунды.
  • Базовые конверсии и “горячие” функции ориентированы на noexcept и отсутствие динамических аллокаций; строковые/парсинговые и часть высокоуровневых хелперов могут выделять память и/или бросать исключения (это указано в документации конкретных функций).

Установка и настройка

Библиотека поставляется в виде заголовков. Для подключения достаточно добавить путь include/time_shield в проект и подключить основной файл:

#include <time_shield.hpp>

Примеры можно собрать скриптом build-examples.bat. Для установки файлов MQL5 предусмотрен install_mql5.bat.

Заметки по интеграции

  • time_shield::time_shield является header-only target и может одновременно использоваться из нескольких статических библиотек в одной программе.
  • NtpTimeService является header-only и поддерживает стандарты C++11/C++14/C++17.
  • На Windows при TIME_SHIELD_ENABLE_NTP_CLIENT=ON экспортируемый CMake target транзитивно подтягивает требуемую socket library.
  • При ручной интеграции без CMake target платформенную socket library нужно добавить самостоятельно.

Подробности см. в docs/library-integration-guidelines-RU.md.

Примеры использования

Ниже приведены небольшие примеры из разделов библиотеки.

Получение и конвертация времени

#include <time_shield.hpp>

using namespace time_shield;

ts_t now = ts();                 // секунды с эпохи
fts_t now_f = fts();             // время в секундах с дробной частью
int ms_part = ms_of_sec(now_f);  // миллисекундная часть
ts_t mono_sec = monotonic_sec(); // монотонные process-local секунды
ts_ms_t mono = monotonic_ms();   // монотонные process-local миллисекунды

Используйте now() / ts_ms() / ts_us() для wall-clock меток времени. Используйте monotonic_sec() / monotonic_ms() / monotonic_us() для измерения интервалов, дедлайнов и timeout-логики.

Форматирование дат

#include <time_shield.hpp>

std::string iso = to_iso8601(now);          // 2024-06-21T12:00:00
std::string local = to_iso8601(now, 2 * SEC_PER_HOUR + 30 * SEC_PER_MIN);
std::string custom = to_string("%Y-%m-%d %H:%M:%S", now);
std::string custom_local = to_string("%Y-%m-%d %H:%M:%S %z", now, 2 * SEC_PER_HOUR);
std::string filename = to_windows_filename(now);

См. examples/time_formatting_example.cpp для компактного cross-platform примера с ISO8601, custom formatting, offset-aware rendering и filename-safe строками. См. examples/time_formatting_showcase_example.cpp для более широкого formatter showcase с UTC/local вариантами, millisecond helpers, MQL5, human-readable и filename-oriented выводом.

Парсинг ISO 8601

#include <time_shield.hpp>

DateTimeStruct dt;
TimeZoneStruct tz;
if (parse_iso8601("2024-11-25T14:30:00-05:30", dt, tz)) {
    ts_t ts_val = to_timestamp(dt) - to_offset(tz);
}

Парсер использует быстрый ручной path без std::regex. Если в строке есть timezone offset, локальное время из строки переводится в канонический UTC instant.

Парсинг по шаблону

#include <time_shield.hpp>

ts_t ts_val = 0;
bool ok = try_parse_format_ts(
    "2024-11-25 14:30:00 -0530",
    "%Y-%m-%d %H:%M:%S %z",
    ts_val);

try_parse_format* понимает ту же custom grammar, что и to_string() / to_string_ms(), остаётся non-throwing и не использует regex-парсинг. %z в formatter выдаёт compact offsets вида +0530, а parser принимает и +HHMM, и ISO-вид +HH:MM. См. examples/time_parser_example.cpp для ISO8601 parsing, seconds/ms/floating timestamp parsing, formatter round-trip и простого failure case.

ISO week date

#include <time_shield.hpp>

using namespace time_shield;

IsoWeekDateStruct iso{};
bool ok_iso = parse_iso_week_date("2025-W512", iso);          // permissive mixed form
bool ok_dt = DateTime::try_parse_iso_week_date("2025w51", iso); // lowercase 'w', Monday default

DateTime monday = DateTime::from_iso_week_date(iso, 9, 30);
std::string custom_iso = to_string_ms("%G-%V-%u", monday.unix_ms(), monday.utc_offset());

Парсер ISO week date принимает canonical формы (YYYY-Www-D, YYYYWwwD) и совместимые mixed separator варианты (YYYY-WwwD, YYYYWww-D). Поддерживаются uppercase и lowercase W, а при отсутствии ISO weekday используется Monday. В custom formatting и parsing %G / %g / %V / %u обозначают ISO week-based tokens. В custom parse format нельзя смешивать ISO week-based year/week tokens с Gregorian %Y / %m / %d.

Класс DateTime

DateTime хранит UTC миллисекунды и фиксированный offset, что позволяет сохранять смещение при форматировании и получать локальные компоненты.

#include <time_shield.hpp>

using namespace time_shield;

DateTime dt = DateTime::parse_iso8601("2025-12-16T10:20:30.123+02:30");
std::string local = dt.to_iso8601();        // сохраняет +02:30
std::string utc = dt.to_iso8601_utc();      // выводит Z
DateTime tomorrow = dt.add_days(1).start_of_day();
int hour_local = dt.hour();                 // локальный час с учётом offset
int hour_utc = dt.utc_hour();               // час в UTC

Класс ZonedClock

ZonedClock хранит переиспользуемый контекст локального времени для именованной зоны или фиксированного UTC offset. Именованные зоны пересчитывают effective offset для заданного UTC-момента, а числовой offset остаётся фиксированным.

#include <time_shield.hpp>

using namespace time_shield;

ZonedClock clock(CET);
DateTime berlin_now = clock.now();

TimeZone zone = UNKNOWN;
bool has_zone = parse_time_zone_name("CET", zone);

TimeZoneStruct tz_struct{};
bool has_offset = parse_time_zone("+05:30", tz_struct);
ZonedClock fixed_clock;
bool created_fixed = ZonedClock::try_from_offset(2 * SEC_PER_HOUR, fixed_clock);

clock.try_set_zone("+05:30");
std::string local_iso = clock.to_iso8601();

ZonedClock ntp_tokyo(JST, true);
ts_ms_t tokyo_local_ms = ntp_tokyo.local_time_ms();

parse_time_zone(...) проверяет синтаксис и принимает числовые смещения вплоть до 23:59. Семантическая проверка поддерживаемых UTC offset для DateTime, ZonedClock и связанных helper-ов использует is_valid_tz_offset(...) и диапазон [-12:00, +14:00].

Преобразование дат OLE Automation (OA)

Преобразования OA совместимы с Excel/COM (базовая дата 1899-12-30), выполняются в UTC и корректно обрабатывают специальную семантику отрицательных дробных OA serials до базовой даты.

#include <time_shield/ole_automation_conversions.hpp>

using namespace time_shield;

oadate_t oa = ts_to_oadate(1714608000);                     // 2024-05-02 00:00:00Z
ts_t ts_from_oa = oadate_to_ts(oa);                         // преобразование обратно в Unix-время
oadate_t pre_base = to_oadate(1899, Month::DEC, 29, 6, 0); // -1.25
oadate_t from_parts = to_oadate(2024, Month::MAY, 2, 12, 0); // 2024-05-02 12:00:00Z

Джулианские даты и лунные вычисления

Хелперы Julian Date используют пролептический григорианский календарь и ориентированы на аналитические значения (JD, MJD, JDN), а не на высокоточные эфемериды. Лунные helper-ы по-прежнему доступны через astronomy entry header.

#include <time_shield/julian_conversions.hpp>
#include <time_shield/astronomy_conversions.hpp>

using namespace time_shield;

jd_t jd = gregorian_ymd_to_jd(2024, 5, 2, 12, 0); // Julian Date для календарного момента
mjd_t mjd = ts_to_mjd(1714608000);                 // Modified Julian Date
jdn_t jdn = gregorian_ymd_to_jdn(2024, 5, 2);     // Julian Day Number (целое значение)
double phase = moon_phase(fts());              // фаза Луны [0..1)
double age_days = moon_age_days(fts());        // примерный возраст Луны в днях
MoonPhaseSineCosine signal = moon_phase_sincos(fts()); // sin/cos фазового угла без скачка в 0/1
MoonQuarterInstants quarters = moon_quarters(fts());   // ближайшие четверти (Unix секунды в double)
bool is_near_new = is_new_moon_window(fts());  // попадание в окно новолуния +/-12ч

Геоцентрический калькулятор фаз Луны

MoonPhaseCalculator (time_shield::astronomy::MoonPhase) выдаёт расширенный набор показателей (освещённость, угловые диаметры, расстояние, фазовый угол), sin/cos для непрерывного фазового сигнала, моменты четвертей и проверки «окон» вокруг фаз. Текущая математика геоцентрическая: положения Солнца/Луны рассчитываются без топоцентрических поправок. Поэтому освещённость и фазовый угол — «глобальные» в данный момент времени, а локально отличается:

  • дата/время из-за часового пояса,
  • ориентация освещённой части (в южном полушарии картинка отражена),
  • наблюдаемость (первый серп, высота над горизонтом, атмосфера).

Текущая математика — геоцентрическая (относительно центра Земли), без топоцентрических поправок/параллакса. Освещённость/фазовый угол глобальны для Земли как целого. По месту реально меняются:

  • локальная дата/время (часовой пояс),
  • ориентация освещённой части (в северном/южном полушарии «картинка» перевёрнута),
  • видимость (первый серп/наблюдаемость) — уже про атмосферу/высоту над горизонтом и т.п.
#include <time_shield/MoonPhase.hpp>

using namespace time_shield;

MoonPhaseCalculator calculator{};
const double ts = 1704067200.0; // 2024-01-01T00:00:00Z
MoonPhaseResult res = calculator.compute(ts);
MoonPhase::quarters_unix_s_t quarters = calculator.quarter_times_unix(ts); // Unix секунды (double)
MoonQuarterInstants around = moon_quarters(ts);
MoonPhaseSineCosine signal = moon_phase_sincos(ts);
bool is_new = calculator.is_new_moon_window(ts); // по умолчанию окно +/-12ч

Конвертация часовых поясов

#include <time_shield.hpp>

ts_t gmt = to_ts(2024, Month::JUN, 21, 12, 0, 0);
ts_t ist = gmt_to_ist(gmt);
ts_t kyiv = gmt_to_kyiv(gmt);
ts_t myt = convert_time_zone(ist, TimeZone::IST, TimeZone::MYT);

Поддерживаются фиксированные зоны IST, MYT, WIB, WITA, WIT, KZT, TRT, BYT, SGT, ICT, PHT, GST, HKT, JST и KST. Для универсального API в секундах используйте zone_to_gmt() / gmt_to_zone() / convert_time_zone(), а для миллисекундных таймстампов — zone_to_gmt_ms() / gmt_to_zone_ms() / convert_time_zone_ms().

NTP‑клиент, пул и сервис времени

#include <time_shield/ntp_client_pool.hpp>
#include <time_shield/ntp_time_service.hpp>

using namespace time_shield;

NtpClientPool pool;
pool.set_default_servers();
pool.measure();
int64_t pool_offset = pool.offset_us();

// Фоновый runner + ленивый сервис через функции-обёртки:
ntp::init(30000); // 30 сек.
int64_t utc_ms = ntp::utc_time_ms();
int64_t offset_us = ntp::offset_us();
int64_t utc_sec = ntp::utc_time_sec();
bool ok = ntp::last_measure_ok();
uint64_t attempts = ntp::measure_count();
ntp::shutdown();

NtpTimeService является header-only и поддерживает стандарты C++11/C++14/C++17. Сервис использует immortal singleton, чтобы избежать проблем порядка разрушения статических объектов. Во время обычной работы геттеры сохраняют ленивый старт. Во время завершения процесса сервис не перезапускает background runner и переходит на fallback через realtime и последний закешированный offset. В общем случае C++17+ позволяет использовать более простой singleton-storage паттерн через inline variables. Для NtpTimeService публичный способ подключения и вызова одинаков в C++11/C++14/C++17.

Документация

Полное описание API и дополнительные примеры доступны по адресу: https://newyaroslav.github.io/time-shield-cpp/

HTML-документация Doxygen публикуется через GitHub Pages.

Лицензия

Проект распространяется по лицензии MIT.