Time Shield Library 2.0.0
C++ library for working with time
Loading...
Searching...
No Matches
ZonedClock.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2#pragma once
3#ifndef TIME_SHIELD_HEADER_TIMEZONE_ZONEDCLOCK_HPP_INCLUDED
4#define TIME_SHIELD_HEADER_TIMEZONE_ZONEDCLOCK_HPP_INCLUDED
5
8
11#include <time_shield/text.hpp>
14
15#if TIME_SHIELD_ENABLE_NTP_CLIENT
16# include <time_shield/ntp.hpp>
17#endif
18
19#include <cstddef>
20#include <stdexcept>
21#include <string>
22
23namespace time_shield {
24
30 class ZonedClock final {
31 public:
33 ZonedClock() noexcept
35 , m_offset(0)
36 , m_is_named_zone(false)
37 , m_use_ntp(false) {}
38
42 explicit ZonedClock(TimeZone zone, bool use_ntp = false) noexcept
44 , m_offset(0)
45 , m_is_named_zone(false)
48 }
49
54 explicit ZonedClock(tz_t utc_offset, bool use_ntp = false)
56 , m_offset(0)
57 , m_is_named_zone(false)
59 if (!try_set_offset(utc_offset)) {
60 throw std::invalid_argument("Invalid UTC offset");
61 }
62 }
63
68 static bool try_from_offset(tz_t utc_offset, ZonedClock& out) noexcept {
69 ZonedClock candidate;
70 if (!candidate.try_set_offset(utc_offset)) {
71 return false;
72 }
73 out = candidate;
74 return true;
75 }
76
79 void set_zone(TimeZone zone) noexcept {
80 if (zone == UNKNOWN) {
82 m_offset = 0;
83 m_is_named_zone = false;
84 return;
85 }
86
87 m_zone = zone;
88 m_offset = 0;
89 m_is_named_zone = true;
90 }
91
95 bool try_set_offset(tz_t utc_offset) noexcept {
96 if (!is_valid_tz_offset(utc_offset)) {
97 return false;
98 }
99
100 m_zone = UNKNOWN;
101 m_offset = utc_offset;
102 m_is_named_zone = false;
103 return true;
104 }
105
109 bool try_set_zone(const std::string& zone_spec) noexcept {
110 const std::string trimmed = trim_ascii(zone_spec);
111 if (trimmed.empty()) {
112 return false;
113 }
114
115 TimeZone parsed_zone = UNKNOWN;
116 if (parse_time_zone_name(trimmed, parsed_zone)) {
117 set_zone(parsed_zone);
118 return true;
119 }
120
121 TimeZoneStruct parsed_offset = create_time_zone_struct(0, 0, true);
122 if (!parse_time_zone(trimmed, parsed_offset)) {
123 return false;
124 }
125
126 return try_set_offset(time_zone_struct_to_offset(parsed_offset));
127 }
128
131 void set_use_ntp(bool use_ntp) noexcept {
133 }
134
136 bool has_named_zone() const noexcept {
137 return m_is_named_zone;
138 }
139
141 TimeZone zone() const noexcept {
142 return m_is_named_zone ? m_zone : UNKNOWN;
143 }
144
146 bool use_ntp() const noexcept {
147 return m_use_ntp;
148 }
149
151 bool ntp_active() const noexcept {
152#if TIME_SHIELD_ENABLE_NTP_CLIENT
154#else
155 return false;
156#endif
157 }
158
160 tz_t offset_now() const noexcept {
162 }
163
167 tz_t offset_at_utc_ms(ts_ms_t utc_ms) const noexcept {
168 tz_t offset = 0;
169 return try_offset_at_utc_ms(utc_ms, offset) ? offset : 0;
170 }
171
176 bool try_offset_at_utc_ms(ts_ms_t utc_ms, tz_t& out) const noexcept {
177 if (utc_ms == ERROR_TIMESTAMP) {
178 return false;
179 }
180
181 if (!m_is_named_zone) {
182 out = m_offset;
183 return true;
184 }
185
186 return zone_offset_at_utc_ms(utc_ms, m_zone, out);
187 }
188
193 if (local_ms == ERROR_TIMESTAMP) {
194 LocalTimeResolution result = {
198 };
199 return result;
200 }
201
202 if (m_is_named_zone) {
204 }
205
206 LocalTimeResolution result = {
210 };
211 return result;
212 }
213
220 ts_ms_t local_ms,
222 NonexistentTimePolicy nonexistent_policy =
223 NonexistentTimePolicy::error) const noexcept {
224 if (m_is_named_zone) {
225 return zone_to_gmt_ms(local_ms,
226 m_zone,
227 ambiguous_policy,
228 nonexistent_policy);
229 }
230
231 return time_shield::to_utc_ms(local_ms, m_offset);
232 }
233
235 ts_t utc_time_sec() const noexcept {
236 return static_cast<ts_t>(current_utc_us() / US_PER_SEC);
237 }
238
240 ts_ms_t utc_time_ms() const noexcept {
241 return current_utc_ms();
242 }
243
245 ts_us_t utc_time_us() const noexcept {
246 return current_utc_us();
247 }
248
250 ts_t local_time_sec() const noexcept {
251 const ts_t utc_sec = utc_time_sec();
252 return utc_sec + static_cast<ts_t>(offset_at_utc_ms(static_cast<ts_ms_t>(utc_sec) * MS_PER_SEC));
253 }
254
256 ts_ms_t local_time_ms() const noexcept {
257 const ts_ms_t utc_ms = current_utc_ms();
258 return utc_ms + static_cast<ts_ms_t>(offset_at_utc_ms(utc_ms)) * MS_PER_SEC;
259 }
260
262 ts_us_t local_time_us() const noexcept {
263 const ts_us_t utc_us = current_utc_us();
264 return utc_us + static_cast<ts_us_t>(offset_at_utc_ms(static_cast<ts_ms_t>(utc_us / MS_PER_SEC))) * US_PER_SEC;
265 }
266
268 DateTime now() const noexcept {
269 return from_utc_ms(current_utc_ms());
270 }
271
275 DateTime from_utc_ms(ts_ms_t utc_ms) const noexcept {
276 return DateTime::from_unix_ms(utc_ms, offset_at_utc_ms(utc_ms));
277 }
278
282 DateTime from_utc_s(ts_t utc_s) const noexcept {
283 return from_utc_ms(static_cast<ts_ms_t>(utc_s) * MS_PER_SEC);
284 }
285
288 std::string zone_name() const {
289 return m_is_named_zone ? std::string(to_cstr(m_zone)) : std::string();
290 }
291
294 std::string zone_full_name() const {
295 if (m_is_named_zone) {
296 return to_str(m_zone, FULL_NAME);
297 }
298 return std::string("UTC") + offset_string_for_offset(m_offset);
299 }
300
302 std::string offset_string() const {
304 }
305
307 std::string to_iso8601() const {
308 return now().to_iso8601();
309 }
310
312 std::string to_iso8601_utc() const {
313 return now().to_iso8601_utc();
314 }
315
319 std::string format(const std::string& fmt) const {
320 return now().format(fmt);
321 }
322
323 private:
324 static std::string trim_ascii(const std::string& value) {
325 std::size_t begin = 0;
326 std::size_t end = value.size();
327 while (begin < end && is_ascii_space(value[begin])) {
328 ++begin;
329 }
330 while (end > begin && is_ascii_space(value[end - 1])) {
331 --end;
332 }
333 return value.substr(begin, end - begin);
334 }
335
336 static bool is_ascii_space(char ch) noexcept {
337 return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\f' || ch == '\v';
338 }
339
340 static std::string offset_string_for_offset(tz_t utc_offset) {
342 }
343
344 ts_ms_t current_utc_ms() const noexcept {
345 return static_cast<ts_ms_t>(current_utc_us() / 1000);
346 }
347
348 ts_us_t current_utc_us() const noexcept {
349#if TIME_SHIELD_ENABLE_NTP_CLIENT
350 if (m_use_ntp) {
351 if (!NtpTimeService::instance().running()) {
352 (void)ntp::init(30000, true);
353 }
354 return static_cast<ts_us_t>(ntp::utc_time_us());
355 }
356#endif
357 return static_cast<ts_us_t>(now_realtime_us());
358 }
359
360 private:
365 };
366
367} // namespace time_shield
368
369#endif // TIME_SHIELD_HEADER_TIMEZONE_ZONEDCLOCK_HPP_INCLUDED
Represents a moment in time with optional fixed UTC offset.
Definition DateTime.mqh:27
std::string to_iso8601() const
Format to ISO8601 string with stored offset.
Definition DateTime.hpp:237
std::string format(const std::string &fmt) const
Format using custom pattern.
Definition DateTime.hpp:247
static DateTime from_unix_ms(ts_ms_t utc_ms, tz_t offset=0) noexcept
Create instance from UTC milliseconds.
Definition DateTime.hpp:37
std::string to_iso8601_utc() const
Format to ISO8601 string in UTC.
Definition DateTime.hpp:242
bool running() const noexcept
Return true when background runner is active.
static NtpTimeServiceT & instance() noexcept
Return the singleton instance.
Stores a target local-time context backed by a named zone or fixed UTC offset.
tz_t offset_at_utc_ms(ts_ms_t utc_ms) const noexcept
Return effective UTC offset in seconds for a specific UTC instant.
static bool is_ascii_space(char ch) noexcept
LocalTimeResolution resolve_local_time_ms(ts_ms_t local_ms) const noexcept
Resolve a local timestamp in this clock's zone.
std::string zone_full_name() const
Return human-readable zone label.
ts_us_t local_time_us() const noexcept
Return current local timestamp in microseconds.
std::string to_iso8601() const
Return current local time formatted as ISO8601 with offset.
DateTime from_utc_s(ts_t utc_s) const noexcept
Return a snapshot for a specific UTC instant in seconds.
ts_t local_time_sec() const noexcept
Return current local timestamp in seconds.
bool try_offset_at_utc_ms(ts_ms_t utc_ms, tz_t &out) const noexcept
Try to resolve effective UTC offset for a UTC instant.
bool ntp_active() const noexcept
Return true when the global NTP service is active for this clock.
static std::string trim_ascii(const std::string &value)
bool try_set_offset(tz_t utc_offset) noexcept
Set the stored fixed UTC offset.
ts_ms_t current_utc_ms() const noexcept
DateTime now() const noexcept
Return current time snapshot with resolved fixed offset.
bool use_ntp() const noexcept
Return the preferred UTC source flag.
bool try_set_zone(const std::string &zone_spec) noexcept
Parse and set a named zone or numeric offset from string.
std::string to_iso8601_utc() const
Return current UTC time formatted as ISO8601 with Z.
static bool try_from_offset(tz_t utc_offset, ZonedClock &out) noexcept
Try to build fixed-offset clock without throwing.
DateTime from_utc_ms(ts_ms_t utc_ms) const noexcept
Return a snapshot for a specific UTC instant in milliseconds.
std::string zone_name() const
Return short name of the stored named zone.
tz_t offset_now() const noexcept
Return effective UTC offset in seconds for the current UTC instant.
std::string format(const std::string &fmt) const
Format current local time using the custom formatter grammar.
ts_ms_t to_utc_ms(ts_ms_t local_ms, AmbiguousTimePolicy ambiguous_policy=AmbiguousTimePolicy::error, NonexistentTimePolicy nonexistent_policy=NonexistentTimePolicy::error) const noexcept
Convert a local timestamp in this clock's zone to UTC.
std::string offset_string() const
Return effective numeric UTC offset as +HH:MM or -HH:MM.
ZonedClock(TimeZone zone, bool use_ntp=false) noexcept
Construct clock for a named zone.
static std::string offset_string_for_offset(tz_t utc_offset)
ZonedClock(tz_t utc_offset, bool use_ntp=false)
Construct clock for a fixed UTC offset.
ts_ms_t local_time_ms() const noexcept
Return current local timestamp in milliseconds.
void set_use_ntp(bool use_ntp) noexcept
Set preferred UTC source.
ts_ms_t utc_time_ms() const noexcept
Return current UTC time in milliseconds.
ts_t utc_time_sec() const noexcept
Return current UTC time in seconds.
void set_zone(TimeZone zone) noexcept
Set the stored named zone.
TimeZone zone() const noexcept
Return stored named zone or UNKNOWN for fixed-offset mode.
bool has_named_zone() const noexcept
Return true when the instance stores a named zone.
ZonedClock() noexcept
Construct UTC fixed-offset clock without NTP.
ts_us_t utc_time_us() const noexcept
Return current UTC time in microseconds.
ts_us_t current_utc_us() const noexcept
@ FULL_NAME
Full name.
Definition enums.hpp:22
bool init(std::chrono::milliseconds interval=std::chrono::seconds(30), bool measure_immediately=true)
Initialize NTP time service and start background measurements.
int64_t utc_time_us() noexcept
Return current UTC time in microseconds based on offset.
constexpr int64_t ERROR_TIMESTAMP
Error timestamp value.
constexpr int64_t MS_PER_SEC
Milliseconds per second.
Definition constants.hpp:77
constexpr int64_t US_PER_SEC
Microseconds per second.
Definition constants.hpp:76
TIME_SHIELD_CONSTEXPR ts_ms_t to_utc_ms(ts_ms_t local_ms, tz_t utc_offset) noexcept
Convert local timestamp (milliseconds) to UTC using UTC offset.
TIME_SHIELD_CONSTEXPR bool is_valid_tz_offset(tz_t off) noexcept
Check if a numeric offset is within supported bounds.
const std::string & to_str(Weekday value, FormatType format=UPPERCASE_NAME)
Converts a Weekday enum value to a string.
Definition enums.hpp:70
const char * to_cstr(Weekday value, FormatType format=UPPERCASE_NAME)
Converts a Weekday enum value to a string.
Definition enums.hpp:43
@ UNKNOWN
Unknown Time Zone.
Definition enums.hpp:202
bool parse_time_zone_name(const char *data, std::size_t length, TimeZone &zone) noexcept
Parse named time zone character buffer into TimeZone enum.
bool parse_time_zone(const char *data, std::size_t length, TimeZoneStruct &tz) noexcept
Parse timezone character buffer into TimeZoneStruct.
TIME_SHIELD_CONSTEXPR tz_t time_zone_struct_to_offset(const TimeZoneStruct &tz) noexcept
Convert a TimeZoneStruct to a numeric UTC offset (seconds).
TimeZoneStruct to_time_zone_struct(tz_t offset)
Converts an integer to a TimeZoneStruct.
std::string time_zone_struct_to_string(const TimeZoneStruct &tz)
Converts a TimeZoneStruct to a string representation.
TimeZoneStruct create_time_zone_struct(int hour, int min, bool is_positive=true)
Creates a TimeZoneStruct instance.
int64_t ts_t
Unix timestamp in seconds since 1970‑01‑01T00:00:00Z.
Definition types.hpp:48
int32_t tz_t
Time zone offset in minutes from UTC (e.g., +180 = UTC+3).
Definition types.hpp:60
int64_t ts_ms_t
Unix timestamp in milliseconds since epoch.
Definition types.hpp:49
int64_t ts_us_t
Unix timestamp in microseconds since epoch.
Definition types.hpp:50
int64_t now_realtime_us()
Get current real time in microseconds using a platform-specific method.
Main namespace for the Time Shield library.
ts_ms_t zone_to_gmt_ms(ts_ms_t local_ms, TimeZone zone)
Convert supported local civil time in milliseconds to GMT (UTC).
@ unsupported
Zone or timestamp cannot be resolved.
@ valid
Local time maps to exactly one UTC timestamp.
bool zone_offset_at_utc_ms(ts_ms_t utc_ms, TimeZone zone, tz_t &out) noexcept
Resolve the effective UTC offset for a UTC millisecond instant.
AmbiguousTimePolicy
Policy for ambiguous local civil timestamps.
@ error
Return ERROR_TIMESTAMP.
LocalTimeResolution resolve_local_time_ms(ts_ms_t local_ms, TimeZone zone)
Resolve local civil time to zero, one, or two UTC candidates.
NonexistentTimePolicy
Policy for nonexistent local civil timestamps.
@ error
Return ERROR_TIMESTAMP.
Result of explicit local-time resolution.
Structure to represent time zone information.
Helpers for converting supported regional time zones and UTC.