Time Shield Library
C++ library for working with time
Loading...
Searching...
No Matches
time_parser.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2#pragma once
3#ifndef _TIME_SHIELD_TIME_PARSER_HPP_INCLUDED
4#define _TIME_SHIELD_TIME_PARSER_HPP_INCLUDED
5
19
20#include "enums.hpp"
21#include "constants.hpp"
22#include "date_time_struct.hpp"
23#include "time_zone_struct.hpp"
24#include "validation.hpp"
25#include "time_conversions.hpp"
28
29#include <algorithm>
30#include <locale>
31#include <array>
32#include <limits>
33#include <stdexcept>
34#include <cctype>
35#include <cstring>
36#include <string>
37
38#if __cplusplus >= 201703L
39# include <string_view>
40#endif
41
42namespace time_shield {
43
75
76 namespace detail {
77
79 inline std::string trim_copy_ascii(const std::string& s) {
80 size_t b = 0;
81 size_t e = s.size();
82 while (b < e && std::isspace(static_cast<unsigned char>(s[b])) != 0) ++b;
83 while (e > b && std::isspace(static_cast<unsigned char>(s[e - 1])) != 0) --e;
84 return s.substr(b, e - b);
85 }
86
87# if __cplusplus >= 201703L
89 inline std::string_view trim_view_ascii(std::string_view v) {
90 size_t b = 0;
91 size_t e = v.size();
92 while (b < e && std::isspace(static_cast<unsigned char>(v[b])) != 0) ++b;
93 while (e > b && std::isspace(static_cast<unsigned char>(v[e - 1])) != 0) --e;
94 return v.substr(b, e - b);
95 }
96# endif
97
101 inline void normalise_month_token_lower(const std::string& month, std::string& output) {
102 output = trim_copy_ascii(month);
103 if (output.empty()) return;
104
105 const auto& facet = std::use_facet<std::ctype<char>>(std::locale());
106 std::transform(output.begin(), output.end(), output.begin(),
107 [&facet](char ch) { return facet.tolower(ch); });
108 }
109
110# if __cplusplus >= 201703L
114 inline void normalise_month_token_lower(std::string_view month, std::string& output) {
115 month = trim_view_ascii(month);
116 output.assign(month.begin(), month.end());
117 if (output.empty()) return;
118
119 const auto& facet = std::use_facet<std::ctype<char>>(std::locale());
120 std::transform(output.begin(), output.end(), output.begin(),
121 [&facet](char ch) { return facet.tolower(ch); });
122 }
123# endif
124
129 inline bool try_parse_month_index(const std::string& month, int& value) {
130 if (month.empty()) return false;
131
132 std::string month_copy;
133 normalise_month_token_lower(month, month_copy);
134 if (month_copy.empty()) return false;
135
136 static const std::array<const char*, MONTHS_PER_YEAR> short_names = {
137 "jan", "feb", "mar", "apr", "may", "jun",
138 "jul", "aug", "sep", "oct", "nov", "dec"
139 };
140 static const std::array<const char*, MONTHS_PER_YEAR> full_names = {
141 "january", "february", "march", "april", "may", "june",
142 "july", "august", "september", "october", "november", "december"
143 };
144
145 for (std::size_t i = 0; i < short_names.size(); ++i) {
146 if (month_copy == short_names[i] || month_copy == full_names[i]) {
147 value = static_cast<int>(i) + 1;
148 return true;
149 }
150 }
151
152 return false;
153 }
154
155# if __cplusplus >= 201703L
160 inline bool try_parse_month_index(std::string_view month, int& value) {
161 if (month.empty()) return false;
162
163 std::string month_copy;
164 normalise_month_token_lower(month, month_copy);
165 if (month_copy.empty()) return false;
166
167 static const std::array<const char*, MONTHS_PER_YEAR> short_names = {
168 "jan", "feb", "mar", "apr", "may", "jun",
169 "jul", "aug", "sep", "oct", "nov", "dec"
170 };
171 static const std::array<const char*, MONTHS_PER_YEAR> full_names = {
172 "january", "february", "march", "april", "may", "june",
173 "july", "august", "september", "october", "november", "december"
174 };
175
176 for (std::size_t i = 0; i < short_names.size(); ++i) {
177 if (month_copy == short_names[i] || month_copy == full_names[i]) {
178 value = static_cast<int>(i) + 1;
179 return true;
180 }
181 }
182
183 return false;
184 }
185# endif
186
191 inline int parse_month_index(const std::string& month) {
192 int value = 0;
193 if (!try_parse_month_index(month, value)) {
194 throw std::invalid_argument("Invalid month name");
195 }
196 return value;
197 }
198
199# if __cplusplus >= 201703L
204 inline int parse_month_index(std::string_view month) {
205 int value = 0;
206 if (!try_parse_month_index(month, value)) {
207 throw std::invalid_argument("Invalid month name");
208 }
209 return value;
210 }
211# endif
212
214 const char* name;
216 };
217
219 inline const std::array<ZoneNameEntry, 25>& time_zone_name_entries() noexcept {
220 static const std::array<ZoneNameEntry, 25> entries = {{
221 {"GMT", GMT},
222 {"UTC", UTC},
223 {"EET", EET},
224 {"CET", CET},
225 {"WET", WET},
226 {"EEST", EEST},
227 {"CEST", CEST},
228 {"WEST", WEST},
229 {"ET", ET},
230 {"CT", CT},
231 {"IST", IST},
232 {"MYT", MYT},
233 {"WIB", WIB},
234 {"WITA", WITA},
235 {"WIT", WIT},
236 {"KZT", KZT},
237 {"TRT", TRT},
238 {"BYT", BYT},
239 {"SGT", SGT},
240 {"ICT", ICT},
241 {"PHT", PHT},
242 {"GST", GST},
243 {"HKT", HKT},
244 {"JST", JST},
245 {"KST", KST}
246 }};
247 return entries;
248 }
249
251 inline bool try_parse_time_zone_name_token(const char* data, std::size_t length, TimeZone& zone) noexcept {
252 if (data == nullptr || length == 0) {
253 zone = UNKNOWN;
254 return false;
255 }
256
257 const std::array<ZoneNameEntry, 25>& entries = time_zone_name_entries();
258 for (std::size_t i = 0; i < entries.size(); ++i) {
259 const std::size_t name_length = std::strlen(entries[i].name);
260 if (length == name_length && std::memcmp(data, entries[i].name, name_length) == 0) {
261 zone = entries[i].zone;
262 return true;
263 }
264 }
265
266 zone = UNKNOWN;
267 return false;
268 }
269
270//------------------------------------------------------------------------------
271// Small C-style helpers (no lambdas, no detail namespace)
272//------------------------------------------------------------------------------
273
275 TIME_SHIELD_CONSTEXPR inline bool is_ascii_space(char c) noexcept {
276 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
277 }
278
280 TIME_SHIELD_CONSTEXPR inline bool is_ascii_digit(char c) noexcept {
281 return c >= '0' && c <= '9';
282 }
283
285 TIME_SHIELD_CONSTEXPR inline bool is_ascii_alpha(char c) noexcept {
286 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
287 }
288
290 TIME_SHIELD_CONSTEXPR inline char ascii_to_lower(char c) noexcept {
291 return (c >= 'A' && c <= 'Z') ? static_cast<char>(c - 'A' + 'a') : c;
292 }
293
295 inline bool ascii_iequals(const char* data, std::size_t length, const char* literal) noexcept {
296 if (data == nullptr || literal == nullptr) {
297 return false;
298 }
299
300 for (std::size_t i = 0; i < length; ++i) {
301 if (literal[i] == '\0' || ascii_to_lower(data[i]) != ascii_to_lower(literal[i])) {
302 return false;
303 }
304 }
305
306 return literal[length] == '\0';
307 }
308
310 inline bool try_parse_positive_int64(const char* data, std::size_t length, int64_t& value) noexcept {
311 value = 0;
312 if (data == nullptr || length == 0) {
313 return false;
314 }
315
316 const int64_t max_value = (std::numeric_limits<int64_t>::max)();
317 for (std::size_t i = 0; i < length; ++i) {
318 if (!is_ascii_digit(data[i])) {
319 return false;
320 }
321
322 const int digit = data[i] - '0';
323 if (value > (max_value - digit) / 10) {
324 value = 0;
325 return false;
326 }
327 value = value * 10 + digit;
328 }
329
330 return value > 0;
331 }
332
334 TIME_SHIELD_CONSTEXPR inline bool try_multiply_positive_int64(int64_t lhs, int64_t rhs, int64_t& value) noexcept {
335 if (lhs <= 0 || rhs <= 0) {
336 return false;
337 }
338
339 if (lhs > (std::numeric_limits<int64_t>::max)() / rhs) {
340 return false;
341 }
342
343 value = lhs * rhs;
344 return true;
345 }
346
348 inline bool try_get_timeframe_unit_seconds_compact(const char* data, std::size_t length, int64_t& unit_seconds) noexcept {
349 unit_seconds = 0;
350 if (data == nullptr || length == 0) {
351 return false;
352 }
353
354 if (length == 1) {
355 switch (ascii_to_lower(data[0])) {
356 case 's': unit_seconds = 1; return true;
357 case 'm': unit_seconds = SEC_PER_MIN; return true;
358 case 'h': unit_seconds = SEC_PER_HOUR; return true;
359 case 'd': unit_seconds = SEC_PER_DAY; return true;
360 case 'w': unit_seconds = SEC_PER_DAY * 7; return true;
361 case 'q': unit_seconds = SEC_PER_DAY * 90; return true;
362 case 'y': unit_seconds = SEC_PER_YEAR; return true;
363 default: return false;
364 }
365 }
366
367 if (length == 2
368 && ascii_to_lower(data[0]) == 'm'
369 && ascii_to_lower(data[1]) == 'n') {
370 unit_seconds = SEC_PER_DAY * 30;
371 return true;
372 }
373
374 return false;
375 }
376
378 inline bool try_get_timeframe_unit_seconds_word(const char* data, std::size_t length, int64_t& unit_seconds) noexcept {
379 unit_seconds = 0;
380 if (data == nullptr || length == 0) {
381 return false;
382 }
383
384 struct TimeframeUnitEntry {
385 const char* name;
386 int64_t seconds;
387 };
388
389 static const TimeframeUnitEntry entries[] = {
390 {"sec", 1},
391 {"second", 1},
392 {"seconds", 1},
393 {"min", SEC_PER_MIN},
394 {"minute", SEC_PER_MIN},
395 {"minutes", SEC_PER_MIN},
396 {"hr", SEC_PER_HOUR},
397 {"hour", SEC_PER_HOUR},
398 {"hours", SEC_PER_HOUR},
399 {"day", SEC_PER_DAY},
400 {"days", SEC_PER_DAY},
401 {"week", SEC_PER_DAY * 7},
402 {"weeks", SEC_PER_DAY * 7},
403 {"month", SEC_PER_DAY * 30},
404 {"months", SEC_PER_DAY * 30},
405 {"quarter", SEC_PER_DAY * 90},
406 {"quarters", SEC_PER_DAY * 90},
407 {"year", SEC_PER_YEAR},
408 {"years", SEC_PER_YEAR}
409 };
410
411 for (std::size_t i = 0; i < sizeof(entries) / sizeof(entries[0]); ++i) {
412 if (ascii_iequals(data, length, entries[i].name)) {
413 unit_seconds = entries[i].seconds;
414 return true;
415 }
416 }
417
418 return false;
419 }
420
422 inline bool try_parse_timeframe_seconds(const char* data, std::size_t length, ts_t& seconds) noexcept {
423 seconds = 0;
424 if (data == nullptr || length == 0) {
425 return false;
426 }
427
428 const char* begin = data;
429 const char* end = data + length;
430 while (begin < end && is_ascii_space(*begin)) {
431 ++begin;
432 }
433 while (end > begin && is_ascii_space(*(end - 1))) {
434 --end;
435 }
436
437 if (begin == end) {
438 return false;
439 }
440
441 int64_t multiplier = 1;
442 int64_t unit_seconds = 0;
443 int64_t result = 0;
444
445 if (is_ascii_digit(*begin)) {
446 const char* cursor = begin;
447 while (cursor < end && is_ascii_digit(*cursor)) {
448 ++cursor;
449 }
450
451 if (!try_parse_positive_int64(begin, static_cast<std::size_t>(cursor - begin), multiplier)) {
452 return false;
453 }
454
455 while (cursor < end && is_ascii_space(*cursor)) {
456 ++cursor;
457 }
458 if (cursor == end) {
459 return false;
460 }
461
462 for (const char* p = cursor; p < end; ++p) {
463 if (!is_ascii_alpha(*p)) {
464 return false;
465 }
466 }
467
468 if (!try_get_timeframe_unit_seconds_word(cursor, static_cast<std::size_t>(end - cursor), unit_seconds)) {
469 return false;
470 }
471
472 if (!try_multiply_positive_int64(multiplier, unit_seconds, result)) {
473 return false;
474 }
475
476 seconds = static_cast<ts_t>(result);
477 return true;
478 }
479
480 if (!is_ascii_alpha(*begin)) {
481 return false;
482 }
483
484 const char* cursor = begin;
485 while (cursor < end && is_ascii_alpha(*cursor)) {
486 ++cursor;
487 }
488
489 if (cursor == end) {
490 if (!try_get_timeframe_unit_seconds_word(begin, static_cast<std::size_t>(end - begin), unit_seconds)) {
491 return false;
492 }
493
494 seconds = static_cast<ts_t>(unit_seconds);
495 return true;
496 }
497
498 if (is_ascii_space(*cursor)) {
499 return false;
500 }
501
502 for (const char* p = cursor; p < end; ++p) {
503 if (!is_ascii_digit(*p)) {
504 return false;
505 }
506 }
507
508 if (!try_get_timeframe_unit_seconds_compact(begin, static_cast<std::size_t>(cursor - begin), unit_seconds)) {
509 return false;
510 }
511 if (!try_parse_positive_int64(cursor, static_cast<std::size_t>(end - cursor), multiplier)) {
512 return false;
513 }
514 if (!try_multiply_positive_int64(multiplier, unit_seconds, result)) {
515 return false;
516 }
517
518 seconds = static_cast<ts_t>(result);
519 return true;
520 }
521
523 TIME_SHIELD_CONSTEXPR inline void skip_spaces(const char*& p, const char* end) noexcept {
524 while (p < end && is_ascii_space(*p)) {
525 ++p;
526 }
527 }
528
531 TIME_SHIELD_CONSTEXPR inline bool parse_2digits(const char*& p, const char* end, int& out) noexcept {
532 if (end - p < 2) {
533 return false;
534 }
535 const char a = p[0];
536 const char b = p[1];
537 if (!is_ascii_digit(a) || !is_ascii_digit(b)) {
538 return false;
539 }
540 out = (a - '0') * 10 + (b - '0');
541 p += 2;
542 return true;
543 }
544
547 TIME_SHIELD_CONSTEXPR inline bool parse_4digits_year(const char*& p, const char* end, year_t& out) noexcept {
548 if (end - p < 4) {
549 return false;
550 }
551 const char a = p[0], b = p[1], c = p[2], d = p[3];
552 if (!is_ascii_digit(a) || !is_ascii_digit(b) || !is_ascii_digit(c) || !is_ascii_digit(d)) {
553 return false;
554 }
555 const int v = (a - '0') * 1000 + (b - '0') * 100 + (c - '0') * 10 + (d - '0');
556 out = static_cast<year_t>(v);
557 p += 4;
558 return true;
559 }
560
564 TIME_SHIELD_CONSTEXPR inline bool parse_fraction_to_ms(const char*& p, const char* end, int& ms_out) noexcept {
565 if (p >= end || !is_ascii_digit(*p)) {
566 return false;
567 }
568
569 int ms = 0;
570 int digits = 0;
571
572 while (p < end && is_ascii_digit(*p)) {
573 if (digits >= 3) {
574 return false;
575 }
576 ms = ms * 10 + (*p - '0');
577 ++digits;
578 ++p;
579 }
580
581 if (digits == 1) {
582 ms *= 100;
583 } else if (digits == 2) {
584 ms *= 10;
585 }
586
587 ms_out = ms;
588 return true;
589 }
590
591 } // namespace detail
592
593//------------------------------------------------------------------------------
594// Month helpers (public)
595//------------------------------------------------------------------------------
596
597// Canonical API (recommended):
598// - parse_month(...) / try_parse_month(...): return month index as int [1..12]
599// - parse_month_enum(...) / try_parse_month_enum(...): return month as enum Month (or any integral/enum T)
600
605 inline bool try_parse_month(const std::string& month, int& value) {
606 return detail::try_parse_month_index(month, value);
607 }
608
613 inline int parse_month(const std::string& month) {
614 return detail::parse_month_index(month);
615 }
616
617#if __cplusplus >= 201703L
622 inline bool try_parse_month(std::string_view month, int& value) {
623 return detail::try_parse_month_index(month, value);
624 }
625
630 inline int parse_month(std::string_view month) {
631 return detail::parse_month_index(month);
632 }
633#endif
634
635// Canonical: parse month -> enum Month (or any T)
636
642 template<class T = Month>
643 inline T parse_month_enum(const std::string& month) {
644 return static_cast<T>(detail::parse_month_index(month));
645 }
646
652 template<class T = Month>
653 inline bool try_parse_month_enum(const std::string& month, T& value) {
654 int idx = 0;
655 if (!detail::try_parse_month_index(month, idx)) return false;
656 value = static_cast<T>(idx);
657 return true;
658 }
659
660#if __cplusplus >= 201703L
666 template<class T = Month>
667 inline T parse_month_enum(std::string_view month) {
668 return static_cast<T>(detail::parse_month_index(month));
669 }
670
676 template<class T = Month>
677 inline bool try_parse_month_enum(std::string_view month, T& value) {
678 int idx = 0;
679 if (!detail::try_parse_month_index(month, idx)) return false;
680 value = static_cast<T>(idx);
681 return true;
682 }
683#endif
684
685// Index aliases (int)
686
691 inline bool try_get_month_index(const std::string& month, int& value) {
692 return try_parse_month(month, value);
693 }
694
699 inline int get_month_index(const std::string& month) {
700 return parse_month(month);
701 }
702
707 inline Month get_month_index_enum(const std::string& month) {
708 return static_cast<Month>(detail::parse_month_index(month));
709 }
710
711#if __cplusplus >= 201703L
713 inline bool try_get_month_index(std::string_view month, int& value) {
714 return try_parse_month(month, value);
715 }
716
718 inline int get_month_index(std::string_view month) {
719 return parse_month(month);
720 }
721
723 inline Month get_month_index_enum(std::string_view month) {
724 return static_cast<Month>(detail::parse_month_index(month));
725 }
726#endif
727
728// Month number aliases (T)
729
735 template<class T = Month>
736 inline T get_month_number(const std::string& month) {
737 return parse_month_enum<T>(month);
738 }
739
741 template<class T = Month>
742 inline T month_of_year(const std::string& month) {
743 return get_month_number<T>(month);
744 }
745
751 template<class T = Month>
752 inline bool try_get_month_number(const std::string& month, T& value) {
753 return try_parse_month_enum<T>(month, value);
754 }
755
757 template<class T = Month>
758 inline bool get_month_number(const std::string& month, T& value) {
759 return try_get_month_number<T>(month, value);
760 }
761
763 template<class T = Month>
764 inline bool month_of_year(const std::string& month, T& value) {
765 return try_get_month_number<T>(month, value);
766 }
767
768#if __cplusplus >= 201703L
770 template<class T = Month>
771 inline T get_month_number(std::string_view month) {
772 return parse_month_enum<T>(month);
773 }
774
776 template<class T = Month>
777 inline T month_of_year(std::string_view month) {
778 return get_month_number<T>(month);
779 }
780
782 template<class T = Month>
783 inline bool try_get_month_number(std::string_view month, T& value) {
784 return try_parse_month_enum<T>(month, value);
785 }
786
788 template<class T = Month>
789 inline bool get_month_number(std::string_view month, T& value) {
790 return try_get_month_number<T>(month, value);
791 }
792
794 template<class T = Month>
795 inline bool month_of_year(std::string_view month, T& value) {
796 return try_get_month_number<T>(month, value);
797 }
798#endif
799
800// const char* overloads to avoid ambiguity with string vs string_view for literals
801
807 template<class T = Month>
808 inline T get_month_number(const char* month) {
809#if __cplusplus >= 201703L
810 return get_month_number<T>(std::string_view(month));
811#else
812 return get_month_number<T>(std::string(month));
813#endif
814 }
815
821 template<class T = Month>
822 inline bool try_get_month_number(const char* month, T& value) {
823#if __cplusplus >= 201703L
824 return try_get_month_number<T>(std::string_view(month), value);
825#else
826 return try_get_month_number<T>(std::string(month), value);
827#endif
828 }
829
831 template<class T = Month>
832 inline T month_of_year(const char* month) {
833 return get_month_number<T>(month);
834 }
835
837 template<class T = Month>
838 inline bool get_month_number(const char* month, T& value) {
839 return try_get_month_number<T>(month, value);
840 }
841
843 template<class T = Month>
844 inline bool month_of_year(const char* month, T& value) {
845 return try_get_month_number<T>(month, value);
846 }
847
848//------------------------------------------------------------------------------
849// Time zone parsing (C-style, high performance)
850//------------------------------------------------------------------------------
851
866 inline bool parse_time_zone(const char* data, std::size_t length, TimeZoneStruct& tz) noexcept {
867 if (!data) {
868 return false;
869 }
870
871 if (length == 0) {
872 tz.hour = 0;
873 tz.min = 0;
874 tz.is_positive = true;
875 return true;
876 }
877
878 if (length == 1 && (data[0] == 'Z' || data[0] == 'z')) {
879 tz.hour = 0;
880 tz.min = 0;
881 tz.is_positive = true;
882 return true;
883 }
884
885 if (length != 6) {
886 return false;
887 }
888
889 const char sign = data[0];
890 if (sign != '+' && sign != '-') {
891 return false;
892 }
893 if (data[3] != ':') {
894 return false;
895 }
896 if (!detail::is_ascii_digit(data[1]) || !detail::is_ascii_digit(data[2]) ||
897 !detail::is_ascii_digit(data[4]) || !detail::is_ascii_digit(data[5])) {
898 return false;
899 }
900
901 tz.is_positive = (sign == '+');
902 tz.hour = (data[1] - '0') * 10 + (data[2] - '0');
903 tz.min = (data[4] - '0') * 10 + (data[5] - '0');
904
905 return is_valid_time_zone(tz);
906 }
907
910 inline bool parse_time_zone(const std::string& tz_str, TimeZoneStruct& tz) noexcept {
911 return parse_time_zone(tz_str.c_str(), tz_str.size(), tz);
912 }
913
915 inline bool parse_tz(const std::string& tz_str, TimeZoneStruct& tz) noexcept {
916 return parse_time_zone(tz_str, tz);
917 }
918
920 inline bool parse_tz(const char* data, std::size_t length, TimeZoneStruct& tz) noexcept {
921 return parse_time_zone(data, length, tz);
922 }
923
930 inline bool parse_time_zone_name(const char* data, std::size_t length, TimeZone& zone) noexcept {
931 if (!data) {
932 zone = UNKNOWN;
933 return false;
934 }
935
936 std::size_t begin = 0;
937 std::size_t end = length;
938 while (begin < end && std::isspace(static_cast<unsigned char>(data[begin])) != 0) {
939 ++begin;
940 }
941 while (end > begin && std::isspace(static_cast<unsigned char>(data[end - 1])) != 0) {
942 --end;
943 }
944 return detail::try_parse_time_zone_name_token(data + begin, end - begin, zone);
945 }
946
949 inline bool parse_time_zone_name(const std::string& value, TimeZone& zone) noexcept {
950 return parse_time_zone_name(value.c_str(), value.size(), zone);
951 }
952
953#if __cplusplus >= 201703L
956 inline bool parse_time_zone_name(std::string_view value, TimeZone& zone) noexcept {
957 return parse_time_zone_name(value.data(), value.size(), zone);
958 }
959#endif
960
963 inline bool parse_time_zone_name(const char* value, TimeZone& zone) noexcept {
964 if (value == nullptr) {
965 zone = UNKNOWN;
966 return false;
967 }
968 return parse_time_zone_name(value, std::strlen(value), zone);
969 }
970
972 inline bool parse_tz_name(const char* data, std::size_t length, TimeZone& zone) noexcept {
973 return parse_time_zone_name(data, length, zone);
974 }
975
977 inline bool parse_tz_name(const std::string& value, TimeZone& zone) noexcept {
978 return parse_time_zone_name(value, zone);
979 }
980
981#if __cplusplus >= 201703L
983 inline bool parse_tz_name(std::string_view value, TimeZone& zone) noexcept {
984 return parse_time_zone_name(value, zone);
985 }
986#endif
987
989 inline bool parse_tz_name(const char* value, TimeZone& zone) noexcept {
990 return parse_time_zone_name(value, zone);
991 }
992
993//------------------------------------------------------------------------------
994// ISO8601 parsing (C-style, no regex, no allocations)
995//------------------------------------------------------------------------------
996
1016 inline bool parse_iso8601(const char* input, std::size_t length,
1017 DateTimeStruct& dt, TimeZoneStruct& tz) noexcept {
1018 if (!input) {
1019 return false;
1020 }
1021
1022 const char* p = input;
1023 const char* end = input + length;
1024
1025 detail::skip_spaces(p, end);
1026
1028 tz = create_time_zone_struct(0, 0);
1029 tz.is_positive = true;
1030
1031 const char* const date_start = p;
1032 const char* date_end = p;
1033 while (date_end < end && *date_end != 'T' && *date_end != 't' && !detail::is_ascii_space(*date_end)) {
1034 ++date_end;
1035 }
1036
1037 bool parsed_iso_week_date = false;
1038 if (date_end > date_start) {
1039 IsoWeekDateStruct iso_date{};
1040 if (parse_iso_week_date(date_start, static_cast<std::size_t>(date_end - date_start), iso_date)) {
1041 const DateStruct calendar_date = iso_week_date_to_date(iso_date);
1042 dt.year = calendar_date.year;
1043 dt.mon = calendar_date.mon;
1044 dt.day = calendar_date.day;
1045 p = date_end;
1046 parsed_iso_week_date = true;
1047 }
1048 }
1049
1050 if (!parsed_iso_week_date) {
1051 // ---- Date: YYYY<sep>MM<sep>DD
1052 if (!detail::parse_4digits_year(p, end, dt.year)) {
1053 return false;
1054 }
1055 if (p >= end) {
1056 return false;
1057 }
1058 const char sep1 = *p;
1059 if (sep1 != '-' && sep1 != '/' && sep1 != '.') {
1060 return false;
1061 }
1062 ++p;
1063
1064 if (!detail::parse_2digits(p, end, dt.mon)) {
1065 return false;
1066 }
1067 if (p >= end) {
1068 return false;
1069 }
1070 const char sep2 = *p;
1071 if (sep2 != '-' && sep2 != '/' && sep2 != '.') {
1072 return false;
1073 }
1074 ++p;
1075
1076 if (!detail::parse_2digits(p, end, dt.day)) {
1077 return false;
1078 }
1079 }
1080
1081 if (!is_valid_date(dt.year, dt.mon, dt.day)) {
1082 return false;
1083 }
1084
1085 // Date-only?
1086 {
1087 const char* q = p;
1088 detail::skip_spaces(q, end);
1089 if (q == end) {
1090 // dt already has time=0 ms=0
1091 return is_valid_date_time(dt);
1092 }
1093 }
1094
1095 // ---- Date/time separator: 'T' or whitespace
1096 if (p >= end) {
1097 return false;
1098 }
1099
1100 if (*p == 'T' || *p == 't') {
1101 ++p;
1102 } else
1103 if (detail::is_ascii_space(*p)) {
1104 // allow one or more spaces
1105 detail::skip_spaces(p, end);
1106 } else {
1107 return false;
1108 }
1109
1110 // ---- Time: hh:mm[:ss][.frac]
1111 if (!detail::parse_2digits(p, end, dt.hour)) {
1112 return false;
1113 }
1114 if (p >= end || *p != ':') {
1115 return false;
1116 }
1117 ++p;
1118
1119 if (!detail::parse_2digits(p, end, dt.min)) {
1120 return false;
1121 }
1122
1123 dt.sec = 0;
1124 dt.ms = 0;
1125 bool has_seconds = false;
1126
1127 // Optional :ss
1128 if (p < end && *p == ':') {
1129 ++p;
1130 if (!detail::parse_2digits(p, end, dt.sec)) {
1131 return false;
1132 }
1133 has_seconds = true;
1134 }
1135
1136 // Optional .fraction (allowed only if we had seconds in original regex,
1137 // but we accept it when seconds are present; for hh:mm (no seconds) we keep it strict).
1138 if (p < end && *p == '.') {
1139 // require seconds field to exist (avoid accepting YYYY-MM-DDThh:mm.xxx)
1140 if (!has_seconds) {
1141 // Ambiguous: could be "hh:mm.fff" which is not in your original formats.
1142 // Keep strict to preserve behavior.
1143 return false;
1144 }
1145
1146 ++p;
1147 int ms = 0;
1148 if (!detail::parse_fraction_to_ms(p, end, ms)) {
1149 return false;
1150 }
1151 dt.ms = ms;
1152 }
1153
1154 // ---- Optional timezone: [spaces] (Z | +/-HH:MM)
1155 detail::skip_spaces(p, end);
1156
1157 if (p < end) {
1158 if (*p == 'Z' || *p == 'z') {
1159 tz.hour = 0;
1160 tz.min = 0;
1161 tz.is_positive = true;
1162 ++p;
1163 } else if (*p == '+' || *p == '-') {
1164 // need 6 chars
1165 if (static_cast<std::size_t>(end - p) < 6) {
1166 return false;
1167 }
1168 if (!parse_time_zone(p, 6, tz)) {
1169 return false;
1170 }
1171 p += 6;
1172 }
1173 }
1174
1175 detail::skip_spaces(p, end);
1176 if (p != end) {
1177 return false;
1178 }
1179
1180 return is_valid_date_time(dt);
1181 }
1182
1185 inline bool parse_iso8601(const std::string& input, DateTimeStruct& dt, TimeZoneStruct& tz) noexcept {
1186 return parse_iso8601(input.c_str(), input.size(), dt, tz);
1187 }
1188
1191 inline bool parse_iso8601(const char* input, DateTimeStruct& dt, TimeZoneStruct& tz) noexcept {
1192 if (input == nullptr) {
1193 return false;
1194 }
1195 return parse_iso8601(input, std::strlen(input), dt, tz);
1196 }
1197
1198# if __cplusplus >= 201703L
1201 inline bool parse_iso8601(std::string_view input, DateTimeStruct& dt, TimeZoneStruct& tz) noexcept {
1202 return parse_iso8601(input.data(), input.size(), dt, tz);
1203 }
1204# endif
1205
1206//------------------------------------------------------------------------------
1207// ISO8601 -> timestamps
1208//------------------------------------------------------------------------------
1209
1214 inline bool str_to_ts(const std::string& str, ts_t& ts) {
1215 DateTimeStruct dt;
1216 TimeZoneStruct tz;
1217 if (!parse_iso8601(str, dt, tz)) return false;
1218 try {
1219 ts = dt_to_timestamp(dt) - to_offset(tz);
1220 return true;
1221 } catch (...) {}
1222 return false;
1223 }
1224
1230 inline bool str_to_ts(const char* data, std::size_t length, ts_t& ts) {
1231 if (!data || length == 0) {
1232 ts = 0;
1233 return false;
1234 }
1235 DateTimeStruct dt;
1236 TimeZoneStruct tz;
1237 if (!parse_iso8601(data, length, dt, tz)) return false;
1238 try {
1239 ts = dt_to_timestamp(dt) - to_offset(tz);
1240 return true;
1241 } catch (...) {}
1242 return false;
1243 }
1244
1249 inline bool str_to_ts_ms(const std::string& str, ts_ms_t& ts) {
1250 DateTimeStruct dt;
1251 TimeZoneStruct tz;
1252 if (!parse_iso8601(str, dt, tz)) return false;
1253 try {
1254 ts = static_cast<ts_ms_t>(dt_to_timestamp_ms(dt)) - sec_to_ms(to_offset(tz));
1255 return true;
1256 } catch (...) {}
1257 return false;
1258 }
1259
1265 inline bool str_to_ts_ms(const char* data, std::size_t length, ts_ms_t& ts) {
1266 if (!data || length == 0) {
1267 ts = 0;
1268 return false;
1269 }
1270 DateTimeStruct dt;
1271 TimeZoneStruct tz;
1272 if (!parse_iso8601(data, length, dt, tz)) return false;
1273 try {
1274 ts = static_cast<ts_ms_t>(dt_to_timestamp_ms(dt)) - sec_to_ms(to_offset(tz));
1275 return true;
1276 } catch (...) {}
1277 return false;
1278 }
1279
1284 inline bool str_to_fts(const std::string& str, fts_t& ts) {
1285 DateTimeStruct dt;
1286 TimeZoneStruct tz;
1287 if (!parse_iso8601(str, dt, tz)) return false;
1288 try {
1289 ts = dt_to_ftimestamp(dt) - static_cast<fts_t>(to_offset(tz));
1290 return true;
1291 } catch (...) {}
1292 return false;
1293 }
1294
1300 inline bool str_to_fts(const char* data, std::size_t length, fts_t& ts) {
1301 if (!data || length == 0) {
1302 ts = 0;
1303 return false;
1304 }
1305 DateTimeStruct dt;
1306 TimeZoneStruct tz;
1307 if (!parse_iso8601(data, length, dt, tz)) return false;
1308 try {
1309 ts = dt_to_ftimestamp(dt) - static_cast<fts_t>(to_offset(tz));
1310 return true;
1311 } catch (...) {}
1312 return false;
1313 }
1314
1315//------------------------------------------------------------------------------
1316// Convenience string -> predicates (workdays)
1317//------------------------------------------------------------------------------
1318
1320 inline bool is_workday(const std::string& str) {
1321 ts_t ts = 0;
1322 if (!str_to_ts(str, ts)) return false;
1323 return is_workday(ts);
1324 }
1325
1327 inline bool is_workday_ms(const std::string& str) {
1328 ts_ms_t ts = 0;
1329 if (!str_to_ts_ms(str, ts)) return false;
1330 return is_workday_ms(ts);
1331 }
1332
1335 inline bool workday(const std::string& str) {
1336 return is_workday(str);
1337 }
1338
1341 inline bool workday_ms(const std::string& str) {
1342 return is_workday_ms(str);
1343 }
1344
1348 inline bool is_first_workday_of_month(const std::string& str) {
1349 ts_t ts = 0;
1350 if (!str_to_ts(str, ts)) return false;
1352 }
1353
1357 inline bool is_first_workday_of_month_ms(const std::string& str) {
1358 ts_ms_t ts = 0;
1359 if (!str_to_ts_ms(str, ts)) return false;
1361 }
1362
1366 inline bool is_last_workday_of_month(const std::string& str) {
1367 ts_t ts = 0;
1368 if (!str_to_ts(str, ts)) return false;
1370 }
1371
1375 inline bool is_last_workday_of_month_ms(const std::string& str) {
1376 ts_ms_t ts = 0;
1377 if (!str_to_ts_ms(str, ts)) return false;
1379 }
1380
1385 inline bool is_within_first_workdays_of_month(const std::string& str, int count) {
1386 ts_t ts = 0;
1387 if (!str_to_ts(str, ts)) return false;
1389 }
1390
1395 inline bool is_within_first_workdays_of_month_ms(const std::string& str, int count) {
1396 ts_ms_t ts = 0;
1397 if (!str_to_ts_ms(str, ts)) return false;
1399 }
1400
1405 inline bool is_within_last_workdays_of_month(const std::string& str, int count) {
1406 ts_t ts = 0;
1407 if (!str_to_ts(str, ts)) return false;
1408 return is_within_last_workdays_of_month(ts, count);
1409 }
1410
1415 inline bool is_within_last_workdays_of_month_ms(const std::string& str, int count) {
1416 ts_ms_t ts = 0;
1417 if (!str_to_ts_ms(str, ts)) return false;
1419 }
1420
1421//------------------------------------------------------------------------------
1422// Convenience: C-string wrappers (non-throwing, ambiguous on failure)
1423//------------------------------------------------------------------------------
1424
1429 inline ts_t ts(const char* str) {
1430 ts_t out = 0;
1431 str_to_ts(str ? std::string(str) : std::string(), out);
1432 return out;
1433 }
1434
1441 inline ts_t ts(const char* data, std::size_t length) {
1442 ts_t out = 0;
1443 if (!str_to_ts(data, length, out)) {
1444 return 0;
1445 }
1446 return out;
1447 }
1448
1453 inline ts_ms_t ts_ms(const char* str) {
1454 ts_ms_t out = 0;
1455 str_to_ts_ms(str ? std::string(str) : std::string(), out);
1456 return out;
1457 }
1458
1464 inline ts_ms_t ts_ms(const char* data, std::size_t length) {
1465 ts_ms_t out = 0;
1466 if (!str_to_ts_ms(data, length, out)) {
1467 return 0;
1468 }
1469 return out;
1470 }
1471
1476 inline fts_t fts(const char* str) {
1477 fts_t out = 0;
1478 str_to_fts(str ? std::string(str) : std::string(), out);
1479 return out;
1480 }
1481
1487 inline fts_t fts(const char* data, std::size_t length) {
1488 fts_t out = 0;
1489 if (!str_to_fts(data, length, out)) {
1490 return 0.0;
1491 }
1492 return out;
1493 }
1494
1495//------------------------------------------------------------------------------
1496
1502 inline ts_t ts(const std::string& str) {
1503 ts_t ts = 0;
1504 str_to_ts(str, ts);
1505 return ts;
1506 }
1507
1513 inline ts_ms_t ts_ms(const std::string& str) {
1514 ts_ms_t ts = 0;
1515 str_to_ts_ms(str, ts);
1516 return ts;
1517 }
1518
1524 inline fts_t fts(const std::string& str) {
1525 fts_t ts = 0;
1526 str_to_fts(str, ts);
1527 return ts;
1528 }
1529
1530//------------------------------------------------------------------------------
1531
1536 inline bool str_to_timeframe_sec(const std::string& str, ts_t& seconds) noexcept {
1537 return detail::try_parse_timeframe_seconds(str.c_str(), str.size(), seconds);
1538 }
1539
1544 inline bool str_to_timeframe_sec(const char* str, ts_t& seconds) noexcept {
1545 if (str == nullptr) {
1546 seconds = 0;
1547 return false;
1548 }
1549 return detail::try_parse_timeframe_seconds(str, std::strlen(str), seconds);
1550 }
1551
1552# if __cplusplus >= 201703L
1557 inline bool str_to_timeframe_sec(std::string_view str, ts_t& seconds) noexcept {
1558 return detail::try_parse_timeframe_seconds(str.data(), str.size(), seconds);
1559 }
1560# endif
1561
1566 inline bool str_to_timeframe_ms(const std::string& str, ts_ms_t& milliseconds) noexcept {
1567 ts_t seconds = 0;
1568 if (!detail::try_parse_timeframe_seconds(str.c_str(), str.size(), seconds)) {
1569 milliseconds = 0;
1570 return false;
1571 }
1572
1573 int64_t milliseconds_value = 0;
1574 if (!detail::try_multiply_positive_int64(seconds, MS_PER_SEC, milliseconds_value)) {
1575 milliseconds = 0;
1576 return false;
1577 }
1578
1579 milliseconds = static_cast<ts_ms_t>(milliseconds_value);
1580 return true;
1581 }
1582
1587 inline bool str_to_timeframe_ms(const char* str, ts_ms_t& milliseconds) noexcept {
1588 if (str == nullptr) {
1589 milliseconds = 0;
1590 return false;
1591 }
1592
1593 ts_t seconds = 0;
1594 if (!detail::try_parse_timeframe_seconds(str, std::strlen(str), seconds)) {
1595 milliseconds = 0;
1596 return false;
1597 }
1598
1599 int64_t milliseconds_value = 0;
1600 if (!detail::try_multiply_positive_int64(seconds, MS_PER_SEC, milliseconds_value)) {
1601 milliseconds = 0;
1602 return false;
1603 }
1604
1605 milliseconds = static_cast<ts_ms_t>(milliseconds_value);
1606 return true;
1607 }
1608
1609# if __cplusplus >= 201703L
1614 inline bool str_to_timeframe_ms(std::string_view str, ts_ms_t& milliseconds) noexcept {
1615 ts_t seconds = 0;
1616 if (!detail::try_parse_timeframe_seconds(str.data(), str.size(), seconds)) {
1617 milliseconds = 0;
1618 return false;
1619 }
1620
1621 int64_t milliseconds_value = 0;
1622 if (!detail::try_multiply_positive_int64(seconds, MS_PER_SEC, milliseconds_value)) {
1623 milliseconds = 0;
1624 return false;
1625 }
1626
1627 milliseconds = static_cast<ts_ms_t>(milliseconds_value);
1628 return true;
1629 }
1630# endif
1631
1636 inline ts_t timeframe_sec(const std::string& str) noexcept {
1637 ts_t seconds = 0;
1638 str_to_timeframe_sec(str, seconds);
1639 return seconds;
1640 }
1641
1646 inline ts_t timeframe_sec(const char* str) noexcept {
1647 ts_t seconds = 0;
1648 str_to_timeframe_sec(str, seconds);
1649 return seconds;
1650 }
1651
1652# if __cplusplus >= 201703L
1657 inline ts_t timeframe_sec(std::string_view str) noexcept {
1658 ts_t seconds = 0;
1659 str_to_timeframe_sec(str, seconds);
1660 return seconds;
1661 }
1662# endif
1663
1668 inline ts_ms_t timeframe_ms(const std::string& str) noexcept {
1669 ts_ms_t milliseconds = 0;
1670 str_to_timeframe_ms(str, milliseconds);
1671 return milliseconds;
1672 }
1673
1678 inline ts_ms_t timeframe_ms(const char* str) noexcept {
1679 ts_ms_t milliseconds = 0;
1680 str_to_timeframe_ms(str, milliseconds);
1681 return milliseconds;
1682 }
1683
1684# if __cplusplus >= 201703L
1689 inline ts_ms_t timeframe_ms(std::string_view str) noexcept {
1690 ts_ms_t milliseconds = 0;
1691 str_to_timeframe_ms(str, milliseconds);
1692 return milliseconds;
1693 }
1694# endif
1695
1696//------------------------------------------------------------------------------
1697
1709 template<class T = int>
1710 inline bool sec_of_day(const std::string& str, T& sec) {
1711 if (str.empty()) return false;
1712
1713 const char* p = str.c_str();
1714 int parts[3] = {0, 0, 0}; // hour, minute, second
1715 int idx = 0;
1716
1717 while (*p && idx < 3) {
1718 // Parse integer
1719 int value = 0;
1720 bool has_digit = false;
1721
1722 while (*p >= '0' && *p <= '9') {
1723 has_digit = true;
1724 value = value * 10 + (*p - '0');
1725 ++p;
1726 }
1727
1728 if (!has_digit) return false;
1729 parts[idx++] = value;
1730
1731 // Expect colon or end
1732 if (*p == ':') {
1733 ++p;
1734 } else if (*p == '\0') {
1735 break;
1736 } else {
1737 return false; // unexpected character
1738 }
1739 }
1740
1741 if (idx == 0) return false;
1742 if (!is_valid_time(parts[0], parts[1], parts[2])) return false;
1743
1744 sec = static_cast<T>(sec_of_day(parts[0], parts[1], parts[2]));
1745 return true;
1746 }
1747
1758 template<class T = int>
1759 inline T sec_of_day(const std::string& str) {
1760 T value{};
1761 if (sec_of_day(str, value))
1762 return value;
1763 return static_cast<T>(SEC_PER_DAY);
1764 }
1765
1767
1768};
1769
1770#endif // _TIME_SHIELD_TIME_PARSER_HPP_INCLUDED
Header file with time-related constants.
Header for date and time structure and related functions.
Header file with enumerations for weekdays, months, and other time-related categories.
constexpr int64_t SEC_PER_YEAR
Seconds per year (365 days).
constexpr int64_t SEC_PER_HOUR
Seconds per hour.
constexpr int64_t MS_PER_SEC
Milliseconds per second.
Definition constants.hpp:77
constexpr int64_t SEC_PER_DAY
Seconds per day.
constexpr int64_t SEC_PER_MIN
Seconds per minute.
TIME_SHIELD_CONSTEXPR bool workday(ts_t ts) noexcept
Alias for is_workday(ts_t).
TIME_SHIELD_CONSTEXPR ts_t ts(year_t year, int month, int day)
Alias for to_timestamp.
DateStruct iso_week_date_to_date(const IsoWeekDateStruct &iso_date)
Convert ISO week date to calendar date.
bool parse_iso_week_date(const char *input, std::size_t length, IsoWeekDateStruct &iso_date) noexcept
Parse ISO week date string buffer.
TIME_SHIELD_CONSTEXPR bool workday_ms(ts_ms_t ts_ms) noexcept
Alias for is_workday(ts_ms_t).
TIME_SHIELD_CONSTEXPR T1 sec_to_ms(T2 ts) noexcept
Converts a timestamp from seconds to milliseconds.
@ JST
Japan Standard Time.
Definition enums.hpp:200
@ GST
Gulf Standard Time.
Definition enums.hpp:198
@ PHT
Philippine Time.
Definition enums.hpp:197
@ BYT
Belarus Time.
Definition enums.hpp:194
@ KST
Korea Standard Time.
Definition enums.hpp:201
@ ET
US Eastern Time.
Definition enums.hpp:185
@ EET
Eastern European Time.
Definition enums.hpp:179
@ CEST
Central European Summer Time.
Definition enums.hpp:183
@ WIT
Eastern Indonesia Time.
Definition enums.hpp:191
@ WEST
Western European Summer Time.
Definition enums.hpp:184
@ WITA
Central Indonesia Time.
Definition enums.hpp:190
@ MYT
Malaysia Time.
Definition enums.hpp:188
@ HKT
Hong Kong Time.
Definition enums.hpp:199
@ WET
Western European Time.
Definition enums.hpp:181
@ KZT
Kazakhstan Time.
Definition enums.hpp:192
@ UNKNOWN
Unknown Time Zone.
Definition enums.hpp:202
@ TRT
Turkey Time.
Definition enums.hpp:193
@ UTC
Coordinated Universal Time.
Definition enums.hpp:178
@ ICT
Indochina Time.
Definition enums.hpp:196
@ GMT
Greenwich Mean Time.
Definition enums.hpp:177
@ SGT
Singapore Time.
Definition enums.hpp:195
@ WIB
Western Indonesia Time.
Definition enums.hpp:189
@ EEST
Eastern European Summer Time.
Definition enums.hpp:182
@ CT
US Central Time.
Definition enums.hpp:186
@ CET
Central European Time.
Definition enums.hpp:180
@ IST
India Standard Time.
Definition enums.hpp:187
bool parse_iso8601(const char *input, std::size_t length, DateTimeStruct &dt, TimeZoneStruct &tz) noexcept
Parse ISO8601 character buffer into DateTimeStruct and TimeZoneStruct.
bool is_last_workday_of_month(const std::string &str)
Parse an ISO8601 string and check if it is the last workday of its month (seconds).
bool str_to_ts_ms(const std::string &str, ts_ms_t &ts)
Convert an ISO8601 string to a millisecond timestamp (ts_ms_t).
bool is_within_last_workdays_of_month_ms(const std::string &str, int count)
Parse ISO8601 string and check if it is within last N workdays of its month (milliseconds).
int parse_month(const std::string &month)
Parse month name token into month index [1..12].
bool try_get_month_index(const std::string &month, int &value)
Try parse month name token into month index [1..12].
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 try_parse_month_enum(const std::string &month, T &value)
Try parse month name token into Month enum (or any T).
bool parse_time_zone(const char *data, std::size_t length, TimeZoneStruct &tz) noexcept
Parse timezone character buffer into TimeZoneStruct.
int get_month_index(const std::string &month)
Parse month name token into month index [1..12].
bool is_first_workday_of_month_ms(const std::string &str)
Parse an ISO8601 string and check if it is the first workday of its month (millisecond precision).
bool try_get_month_number(const std::string &month, T &value)
Try get the month number by name, with output parameter.
bool str_to_timeframe_ms(const std::string &str, ts_ms_t &milliseconds) noexcept
Parse timeframe string into fixed milliseconds.
Month get_month_index_enum(const std::string &month)
Parse month name token into Month enum.
bool is_within_first_workdays_of_month_ms(const std::string &str, int count)
Parse an ISO8601 string and check if it falls within the first N workdays of its month (millisecond p...
bool is_workday(const std::string &str)
Parse ISO8601 string and check if it falls on a workday (seconds precision).
bool sec_of_day(const std::string &str, T &sec)
Parse time of day string to seconds of day.
bool is_last_workday_of_month_ms(const std::string &str)
Parse an ISO8601 string and check if it is the last workday of its month (millisecond).
bool str_to_timeframe_sec(const std::string &str, ts_t &seconds) noexcept
Parse timeframe string into fixed seconds.
bool parse_tz(const std::string &tz_str, TimeZoneStruct &tz) noexcept
Alias for parse_time_zone.
bool parse_tz_name(const char *data, std::size_t length, TimeZone &zone) noexcept
Alias for parse_time_zone_name.
bool is_within_first_workdays_of_month(const std::string &str, int count)
Parse an ISO8601 string and check if it falls within the first N workdays of its month.
bool is_first_workday_of_month(const std::string &str)
Parse ISO8601 string and check if it is the first workday of its month (seconds).
bool try_parse_month(const std::string &month, int &value)
Try parse month name token into month index [1..12].
T get_month_number(const std::string &month)
Get the month number by name (throwing).
bool str_to_ts(const std::string &str, ts_t &ts)
Convert an ISO8601 string to a timestamp (ts_t).
bool str_to_fts(const std::string &str, fts_t &ts)
Convert an ISO8601 string to a floating-point timestamp (fts_t).
ts_t timeframe_sec(const std::string &str) noexcept
Convert timeframe string to fixed seconds.
ts_ms_t timeframe_ms(const std::string &str) noexcept
Convert timeframe string to fixed milliseconds.
bool is_within_last_workdays_of_month(const std::string &str, int count)
Parse ISO8601 string and check if it is within last N workdays of its month (seconds).
T parse_month_enum(const std::string &month)
Parse month name token into Month enum (throwing).
bool is_workday_ms(const std::string &str)
Parse ISO8601 string and check if it falls on a workday (milliseconds precision).
TIME_SHIELD_CONSTEXPR tz_t to_offset(const TimeZoneStruct &tz) noexcept
Alias for time_zone_struct_to_offset.
TIME_SHIELD_CONSTEXPR fts_t dt_to_ftimestamp(const T &date_time)
Converts a date-time structure to a floating-point timestamp.
TIME_SHIELD_CONSTEXPR T month_of_year(ts_t ts) noexcept
Get the month of the year.
TIME_SHIELD_CONSTEXPR ts_ms_t dt_to_timestamp_ms(const T &date_time)
Converts a date-time structure to a timestamp in milliseconds.
TIME_SHIELD_CONSTEXPR ts_t dt_to_timestamp(const T &date_time)
Converts a date-time structure to a timestamp.
const DateTimeStruct create_date_time_struct(int64_t year, int mon=1, int day=1, int hour=0, int min=0, int sec=0, int ms=0)
Creates a DateTimeStruct instance.
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
int64_t ts_ms_t
Unix timestamp in milliseconds since epoch.
Definition types.hpp:50
double fts_t
Floating-point timestamp (fractional seconds since epoch).
Definition types.hpp:52
int64_t year_t
Year as an integer (e.g., 2024).
Definition types.hpp:41
ts_t ts() noexcept
Get the current UTC timestamp in seconds.
ts_ms_t ts_ms() noexcept
Get the current UTC timestamp in milliseconds.
fts_t fts() noexcept
Get the current UTC timestamp in floating-point seconds.
TIME_SHIELD_CONSTEXPR bool is_valid_time_zone(T hour, T min) noexcept
Check if the time zone is valid.
TIME_SHIELD_CONSTEXPR bool is_valid_date(T1 year, T2 month, T2 day) noexcept
Checks the correctness of the specified date.
TIME_SHIELD_CONSTEXPR bool is_valid_time(T1 hour, T1 min, T1 sec, T2 ms=0) noexcept
Checks the correctness of the specified time.
TIME_SHIELD_CONSTEXPR bool is_valid_date_time(T1 year, T2 month, T2 day, T2 hour=0, T2 min=0, T2 sec=0, T3 ms=0) noexcept
Checks the correctness of a date and time.
Conversions and utilities for ISO week dates (ISO 8601).
bool try_parse_timeframe_seconds(const char *data, std::size_t length, ts_t &seconds) noexcept
Parse timeframe string into fixed seconds.
bool try_get_timeframe_unit_seconds_compact(const char *data, std::size_t length, int64_t &unit_seconds) noexcept
Resolve compact trading unit token to seconds.
TIME_SHIELD_CONSTEXPR bool is_ascii_alpha(char c) noexcept
Check whether character is ASCII letter.
bool try_parse_positive_int64(const char *data, std::size_t length, int64_t &value) noexcept
Parse positive int64 value from ASCII digits.
TIME_SHIELD_CONSTEXPR bool try_multiply_positive_int64(int64_t lhs, int64_t rhs, int64_t &value) noexcept
Multiply positive int64 values with overflow check.
TIME_SHIELD_CONSTEXPR bool parse_fraction_to_ms(const char *&p, const char *end, int &ms_out) noexcept
Parse fractional seconds (1..9 digits) and convert to milliseconds.
TIME_SHIELD_CONSTEXPR char ascii_to_lower(char c) noexcept
Convert ASCII letter to lower-case.
TIME_SHIELD_CONSTEXPR bool is_ascii_digit(char c) noexcept
Check whether character is ASCII digit.
TIME_SHIELD_CONSTEXPR bool parse_2digits(const char *&p, const char *end, int &out) noexcept
Parse exactly 2 digits into int.
TIME_SHIELD_CONSTEXPR bool is_ascii_space(char c) noexcept
Check whether character is ASCII whitespace.
void normalise_month_token_lower(const std::string &month, std::string &output)
Normalize month token to lower-case ASCII using current locale facet.
const std::array< ZoneNameEntry, 25 > & time_zone_name_entries() noexcept
Return supported strict named-zone entries.
bool try_get_timeframe_unit_seconds_word(const char *data, std::size_t length, int64_t &unit_seconds) noexcept
Resolve word timeframe unit token to seconds.
std::string trim_copy_ascii(const std::string &s)
Trim ASCII whitespace from both ends.
TIME_SHIELD_CONSTEXPR void skip_spaces(const char *&p, const char *end) noexcept
Skip ASCII whitespace.
bool try_parse_time_zone_name_token(const char *data, std::size_t length, TimeZone &zone) noexcept
Parse strict named-zone token without trimming.
TIME_SHIELD_CONSTEXPR bool parse_4digits_year(const char *&p, const char *end, year_t &out) noexcept
Parse exactly 4 digits into year_t (via int).
std::string_view trim_view_ascii(std::string_view v)
Trim ASCII whitespace from both ends (string_view).
bool try_parse_month_index(const std::string &month, int &value)
Try parse month name token into month index (1..12).
bool ascii_iequals(const char *data, std::size_t length, const char *literal) noexcept
Compare ASCII token to literal case-insensitively.
int parse_month_index(const std::string &month)
Parse month name token into month index (1..12).
Main namespace for the Time Shield library.
Structure to represent a date.
int32_t mon
Month component of the date (1-12).
int32_t day
Day component of the date (1-31).
int64_t year
Year component of the date.
Structure to represent date and time.
Structure to represent an ISO week date.
Structure to represent time zone information.
Umbrella header for time conversion functions.
Header file for fast parsing with formatter-compatible custom patterns.
Header for time zone structure and related functions.
Header file with time-related validation functions.