Time Shield Library
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_ZONED_CLOCK_HPP_INCLUDED
4#define _TIME_SHIELD_ZONED_CLOCK_HPP_INCLUDED
5
8
9#include "config.hpp"
10#include "constants.hpp"
11#include "DateTime.hpp"
12#include "enums.hpp"
13#include "time_parser.hpp"
14#include "time_utils.hpp"
17
18#if TIME_SHIELD_ENABLE_NTP_CLIENT
19# include "ntp_time_service.hpp"
20#endif
21
22#include <cstddef>
23#include <stdexcept>
24#include <string>
25
26namespace time_shield {
27
33 class ZonedClock final {
34 public:
36 ZonedClock() noexcept
38 , m_offset(0)
39 , m_is_named_zone(false)
40 , m_use_ntp(false) {}
41
45 explicit ZonedClock(TimeZone zone, bool use_ntp = false) noexcept
47 , m_offset(0)
48 , m_is_named_zone(false)
51 }
52
57 explicit ZonedClock(tz_t utc_offset, bool use_ntp = false)
59 , m_offset(0)
60 , m_is_named_zone(false)
62 if (!try_set_offset(utc_offset)) {
63 throw std::invalid_argument("Invalid UTC offset");
64 }
65 }
66
71 static bool try_from_offset(tz_t utc_offset, ZonedClock& out) noexcept {
72 ZonedClock candidate;
73 if (!candidate.try_set_offset(utc_offset)) {
74 return false;
75 }
76 out = candidate;
77 return true;
78 }
79
82 void set_zone(TimeZone zone) noexcept {
83 if (zone == UNKNOWN) {
85 m_offset = 0;
86 m_is_named_zone = false;
87 return;
88 }
89
90 m_zone = zone;
91 m_offset = 0;
92 m_is_named_zone = true;
93 }
94
98 bool try_set_offset(tz_t utc_offset) noexcept {
99 if (!is_valid_tz_offset(utc_offset)) {
100 return false;
101 }
102
103 m_zone = UNKNOWN;
104 m_offset = utc_offset;
105 m_is_named_zone = false;
106 return true;
107 }
108
112 bool try_set_zone(const std::string& zone_spec) noexcept {
113 const std::string trimmed = trim_ascii(zone_spec);
114 if (trimmed.empty()) {
115 return false;
116 }
117
118 TimeZone parsed_zone = UNKNOWN;
119 if (parse_time_zone_name(trimmed, parsed_zone)) {
120 set_zone(parsed_zone);
121 return true;
122 }
123
124 TimeZoneStruct parsed_offset = create_time_zone_struct(0, 0, true);
125 if (!parse_time_zone(trimmed, parsed_offset)) {
126 return false;
127 }
128
129 return try_set_offset(time_zone_struct_to_offset(parsed_offset));
130 }
131
134 void set_use_ntp(bool use_ntp) noexcept {
136 }
137
139 bool has_named_zone() const noexcept {
140 return m_is_named_zone;
141 }
142
144 TimeZone zone() const noexcept {
145 return m_is_named_zone ? m_zone : UNKNOWN;
146 }
147
149 bool use_ntp() const noexcept {
150 return m_use_ntp;
151 }
152
154 bool ntp_active() const noexcept {
155#if TIME_SHIELD_ENABLE_NTP_CLIENT
157#else
158 return false;
159#endif
160 }
161
163 tz_t offset_now() const noexcept {
165 }
166
170 tz_t offset_at_utc_ms(ts_ms_t utc_ms) const noexcept {
171 tz_t offset = 0;
172 return try_offset_at_utc_ms(utc_ms, offset) ? offset : 0;
173 }
174
179 bool try_offset_at_utc_ms(ts_ms_t utc_ms, tz_t& out) const noexcept {
180 if (utc_ms == ERROR_TIMESTAMP) {
181 return false;
182 }
183
184 if (!m_is_named_zone) {
185 out = m_offset;
186 return true;
187 }
188
189 return zone_offset_at_utc_ms(utc_ms, m_zone, out);
190 }
191
196 if (local_ms == ERROR_TIMESTAMP) {
197 LocalTimeResolution result = {
201 };
202 return result;
203 }
204
205 if (m_is_named_zone) {
207 }
208
209 LocalTimeResolution result = {
213 };
214 return result;
215 }
216
223 ts_ms_t local_ms,
225 NonexistentTimePolicy nonexistent_policy =
226 NonexistentTimePolicy::error) const noexcept {
227 if (m_is_named_zone) {
228 return zone_to_gmt_ms(local_ms,
229 m_zone,
230 ambiguous_policy,
231 nonexistent_policy);
232 }
233
234 return time_shield::to_utc_ms(local_ms, m_offset);
235 }
236
238 ts_t utc_time_sec() const noexcept {
239 return static_cast<ts_t>(current_utc_us() / US_PER_SEC);
240 }
241
243 ts_ms_t utc_time_ms() const noexcept {
244 return current_utc_ms();
245 }
246
248 ts_us_t utc_time_us() const noexcept {
249 return current_utc_us();
250 }
251
253 ts_t local_time_sec() const noexcept {
254 const ts_t utc_sec = utc_time_sec();
255 return utc_sec + static_cast<ts_t>(offset_at_utc_ms(static_cast<ts_ms_t>(utc_sec) * MS_PER_SEC));
256 }
257
259 ts_ms_t local_time_ms() const noexcept {
260 const ts_ms_t utc_ms = current_utc_ms();
261 return utc_ms + static_cast<ts_ms_t>(offset_at_utc_ms(utc_ms)) * MS_PER_SEC;
262 }
263
265 ts_us_t local_time_us() const noexcept {
266 const ts_us_t utc_us = current_utc_us();
267 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;
268 }
269
271 DateTime now() const noexcept {
272 return from_utc_ms(current_utc_ms());
273 }
274
278 DateTime from_utc_ms(ts_ms_t utc_ms) const noexcept {
279 return DateTime::from_unix_ms(utc_ms, offset_at_utc_ms(utc_ms));
280 }
281
285 DateTime from_utc_s(ts_t utc_s) const noexcept {
286 return from_utc_ms(static_cast<ts_ms_t>(utc_s) * MS_PER_SEC);
287 }
288
291 std::string zone_name() const {
292 return m_is_named_zone ? std::string(to_cstr(m_zone)) : std::string();
293 }
294
297 std::string zone_full_name() const {
298 if (m_is_named_zone) {
299 return to_str(m_zone, FULL_NAME);
300 }
301 return std::string("UTC") + offset_string_for_offset(m_offset);
302 }
303
305 std::string offset_string() const {
307 }
308
310 std::string to_iso8601() const {
311 return now().to_iso8601();
312 }
313
315 std::string to_iso8601_utc() const {
316 return now().to_iso8601_utc();
317 }
318
322 std::string format(const std::string& fmt) const {
323 return now().format(fmt);
324 }
325
326 private:
327 static std::string trim_ascii(const std::string& value) {
328 std::size_t begin = 0;
329 std::size_t end = value.size();
330 while (begin < end && is_ascii_space(value[begin])) {
331 ++begin;
332 }
333 while (end > begin && is_ascii_space(value[end - 1])) {
334 --end;
335 }
336 return value.substr(begin, end - begin);
337 }
338
339 static bool is_ascii_space(char ch) noexcept {
340 return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\f' || ch == '\v';
341 }
342
343 static std::string offset_string_for_offset(tz_t utc_offset) {
345 }
346
347 ts_ms_t current_utc_ms() const noexcept {
348 return static_cast<ts_ms_t>(current_utc_us() / 1000);
349 }
350
351 ts_us_t current_utc_us() const noexcept {
352#if TIME_SHIELD_ENABLE_NTP_CLIENT
353 if (m_use_ntp) {
354 if (!NtpTimeService::instance().running()) {
355 (void)ntp::init(30000, true);
356 }
357 return static_cast<ts_us_t>(ntp::utc_time_us());
358 }
359#endif
360 return static_cast<ts_us_t>(now_realtime_us());
361 }
362
363 private:
368 };
369
370} // namespace time_shield
371
372#endif // _TIME_SHIELD_ZONED_CLOCK_HPP_INCLUDED
Value-type wrapper for timestamps with fixed UTC offset.
Represents a moment in time with optional fixed UTC offset.
Definition DateTime.hpp:37
std::string to_iso8601() const
Format to ISO8601 string with stored offset.
Definition DateTime.hpp:248
std::string format(const std::string &fmt) const
Format using custom pattern.
Definition DateTime.hpp:258
static DateTime from_unix_ms(ts_ms_t utc_ms, tz_t offset=0) noexcept
Create instance from UTC milliseconds.
Definition DateTime.hpp:48
std::string to_iso8601_utc() const
Format to ISO8601 string in UTC.
Definition DateTime.hpp:253
bool running() const noexcept
Return true when background runner is active.
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
Configuration macros for the library.
Header file with time-related constants.
Header file with enumerations for weekdays, months, and other time-related categories.
@ 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:49
int32_t tz_t
Time zone offset in minutes from UTC (e.g., +180 = UTC+3).
Definition types.hpp:61
int64_t ts_ms_t
Unix timestamp in milliseconds since epoch.
Definition types.hpp:50
int64_t ts_us_t
Unix timestamp in microseconds since epoch.
Definition types.hpp:51
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.
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.
Result of explicit local-time resolution.
Structure to represent time zone information.
Header file with functions for parsing dates and times in ISO8601 format and converting them to vario...
Header file with time-related utility functions.
Helpers for converting supported regional time zones and UTC.
Conversions between numeric offsets and TimeZoneStruct.