Time Shield Library 2.0.0
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_HEADER_TEXT_TIME_PARSER_HPP_INCLUDED
4#define TIME_SHIELD_HEADER_TEXT_TIME_PARSER_HPP_INCLUDED
5
19
20#include <time_shield/core.hpp>
23
24#include <algorithm>
25#include <locale>
26#include <array>
27#include <limits>
28#include <stdexcept>
29#include <cctype>
30#include <cstring>
31#include <string>
32
33#if __cplusplus >= 201703L
34# include <string_view>
35#endif
36
37namespace time_shield {
38
70
71 namespace detail {
72
74 inline std::string trim_copy_ascii(const std::string& s) {
75 size_t b = 0;
76 size_t e = s.size();
77 while (b < e && std::isspace(static_cast<unsigned char>(s[b])) != 0) ++b;
78 while (e > b && std::isspace(static_cast<unsigned char>(s[e - 1])) != 0) --e;
79 return s.substr(b, e - b);
80 }
81
82# if __cplusplus >= 201703L
84 inline std::string_view trim_view_ascii(std::string_view v) {
85 size_t b = 0;
86 size_t e = v.size();
87 while (b < e && std::isspace(static_cast<unsigned char>(v[b])) != 0) ++b;
88 while (e > b && std::isspace(static_cast<unsigned char>(v[e - 1])) != 0) --e;
89 return v.substr(b, e - b);
90 }
91# endif
92
96 inline void normalise_month_token_lower(const std::string& month, std::string& output) {
97 output = trim_copy_ascii(month);
98 if (output.empty()) return;
99
100 const auto& facet = std::use_facet<std::ctype<char>>(std::locale());
101 std::transform(output.begin(), output.end(), output.begin(),
102 [&facet](char ch) { return facet.tolower(ch); });
103 }
104
105# if __cplusplus >= 201703L
109 inline void normalise_month_token_lower(std::string_view month, std::string& output) {
110 month = trim_view_ascii(month);
111 output.assign(month.begin(), month.end());
112 if (output.empty()) return;
113
114 const auto& facet = std::use_facet<std::ctype<char>>(std::locale());
115 std::transform(output.begin(), output.end(), output.begin(),
116 [&facet](char ch) { return facet.tolower(ch); });
117 }
118# endif
119
124 inline bool try_parse_month_index(const std::string& month, int& value) {
125 if (month.empty()) return false;
126
127 std::string month_copy;
128 normalise_month_token_lower(month, month_copy);
129 if (month_copy.empty()) return false;
130
131 static const std::array<const char*, MONTHS_PER_YEAR> short_names = {
132 "jan", "feb", "mar", "apr", "may", "jun",
133 "jul", "aug", "sep", "oct", "nov", "dec"
134 };
135 static const std::array<const char*, MONTHS_PER_YEAR> full_names = {
136 "january", "february", "march", "april", "may", "june",
137 "july", "august", "september", "october", "november", "december"
138 };
139
140 for (std::size_t i = 0; i < short_names.size(); ++i) {
141 if (month_copy == short_names[i] || month_copy == full_names[i]) {
142 value = static_cast<int>(i) + 1;
143 return true;
144 }
145 }
146
147 return false;
148 }
149
150# if __cplusplus >= 201703L
155 inline bool try_parse_month_index(std::string_view month, int& value) {
156 if (month.empty()) return false;
157
158 std::string month_copy;
159 normalise_month_token_lower(month, month_copy);
160 if (month_copy.empty()) return false;
161
162 static const std::array<const char*, MONTHS_PER_YEAR> short_names = {
163 "jan", "feb", "mar", "apr", "may", "jun",
164 "jul", "aug", "sep", "oct", "nov", "dec"
165 };
166 static const std::array<const char*, MONTHS_PER_YEAR> full_names = {
167 "january", "february", "march", "april", "may", "june",
168 "july", "august", "september", "october", "november", "december"
169 };
170
171 for (std::size_t i = 0; i < short_names.size(); ++i) {
172 if (month_copy == short_names[i] || month_copy == full_names[i]) {
173 value = static_cast<int>(i) + 1;
174 return true;
175 }
176 }
177
178 return false;
179 }
180# endif
181
186 inline int parse_month_index(const std::string& month) {
187 int value = 0;
188 if (!try_parse_month_index(month, value)) {
189 throw std::invalid_argument("Invalid month name");
190 }
191 return value;
192 }
193
194# if __cplusplus >= 201703L
199 inline int parse_month_index(std::string_view month) {
200 int value = 0;
201 if (!try_parse_month_index(month, value)) {
202 throw std::invalid_argument("Invalid month name");
203 }
204 return value;
205 }
206# endif
207
209 const char* name;
211 };
212
214 inline const std::array<ZoneNameEntry, 25>& time_zone_name_entries() noexcept {
215 static const std::array<ZoneNameEntry, 25> entries = {{
216 {"GMT", GMT},
217 {"UTC", UTC},
218 {"EET", EET},
219 {"CET", CET},
220 {"WET", WET},
221 {"EEST", EEST},
222 {"CEST", CEST},
223 {"WEST", WEST},
224 {"ET", ET},
225 {"CT", CT},
226 {"IST", IST},
227 {"MYT", MYT},
228 {"WIB", WIB},
229 {"WITA", WITA},
230 {"WIT", WIT},
231 {"KZT", KZT},
232 {"TRT", TRT},
233 {"BYT", BYT},
234 {"SGT", SGT},
235 {"ICT", ICT},
236 {"PHT", PHT},
237 {"GST", GST},
238 {"HKT", HKT},
239 {"JST", JST},
240 {"KST", KST}
241 }};
242 return entries;
243 }
244
246 inline bool try_parse_time_zone_name_token(const char* data, std::size_t length, TimeZone& zone) noexcept {
247 if (data == nullptr || length == 0) {
248 zone = UNKNOWN;
249 return false;
250 }
251
252 const std::array<ZoneNameEntry, 25>& entries = time_zone_name_entries();
253 for (std::size_t i = 0; i < entries.size(); ++i) {
254 const std::size_t name_length = std::strlen(entries[i].name);
255 if (length == name_length && std::memcmp(data, entries[i].name, name_length) == 0) {
256 zone = entries[i].zone;
257 return true;
258 }
259 }
260
261 zone = UNKNOWN;
262 return false;
263 }
264
265//------------------------------------------------------------------------------
266// Small C-style helpers (no lambdas, no detail namespace)
267//------------------------------------------------------------------------------
268
270 TIME_SHIELD_CONSTEXPR inline bool is_ascii_space(char c) noexcept {
271 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
272 }
273
275 TIME_SHIELD_CONSTEXPR inline bool is_ascii_digit(char c) noexcept {
276 return c >= '0' && c <= '9';
277 }
278
280 TIME_SHIELD_CONSTEXPR inline bool is_ascii_alpha(char c) noexcept {
281 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
282 }
283
285 TIME_SHIELD_CONSTEXPR inline char ascii_to_lower(char c) noexcept {
286 return (c >= 'A' && c <= 'Z') ? static_cast<char>(c - 'A' + 'a') : c;
287 }
288
290 inline bool ascii_iequals(const char* data, std::size_t length, const char* literal) noexcept {
291 if (data == nullptr || literal == nullptr) {
292 return false;
293 }
294
295 for (std::size_t i = 0; i < length; ++i) {
296 if (literal[i] == '\0' || ascii_to_lower(data[i]) != ascii_to_lower(literal[i])) {
297 return false;
298 }
299 }
300
301 return literal[length] == '\0';
302 }
303
305 inline bool try_parse_positive_int64(const char* data, std::size_t length, int64_t& value) noexcept {
306 value = 0;
307 if (data == nullptr || length == 0) {
308 return false;
309 }
310
311 const int64_t max_value = (std::numeric_limits<int64_t>::max)();
312 for (std::size_t i = 0; i < length; ++i) {
313 if (!is_ascii_digit(data[i])) {
314 return false;
315 }
316
317 const int digit = data[i] - '0';
318 if (value > (max_value - digit) / 10) {
319 value = 0;
320 return false;
321 }
322 value = value * 10 + digit;
323 }
324
325 return value > 0;
326 }
327
329 TIME_SHIELD_CONSTEXPR inline bool try_multiply_positive_int64(int64_t lhs, int64_t rhs, int64_t& value) noexcept {
330 if (lhs <= 0 || rhs <= 0) {
331 return false;
332 }
333
334 if (lhs > (std::numeric_limits<int64_t>::max)() / rhs) {
335 return false;
336 }
337
338 value = lhs * rhs;
339 return true;
340 }
341
343 inline bool try_get_timeframe_unit_seconds_compact(const char* data, std::size_t length, int64_t& unit_seconds) noexcept {
344 unit_seconds = 0;
345 if (data == nullptr || length == 0) {
346 return false;
347 }
348
349 if (length == 1) {
350 switch (ascii_to_lower(data[0])) {
351 case 's': unit_seconds = 1; return true;
352 case 'm': unit_seconds = SEC_PER_MIN; return true;
353 case 'h': unit_seconds = SEC_PER_HOUR; return true;
354 case 'd': unit_seconds = SEC_PER_DAY; return true;
355 case 'w': unit_seconds = SEC_PER_DAY * 7; return true;
356 case 'q': unit_seconds = SEC_PER_DAY * 90; return true;
357 case 'y': unit_seconds = SEC_PER_YEAR; return true;
358 default: return false;
359 }
360 }
361
362 if (length == 2
363 && ascii_to_lower(data[0]) == 'm'
364 && ascii_to_lower(data[1]) == 'n') {
365 unit_seconds = SEC_PER_DAY * 30;
366 return true;
367 }
368
369 return false;
370 }
371
373 inline bool try_get_timeframe_unit_seconds_word(const char* data, std::size_t length, int64_t& unit_seconds) noexcept {
374 unit_seconds = 0;
375 if (data == nullptr || length == 0) {
376 return false;
377 }
378
379 struct TimeframeUnitEntry {
380 const char* name;
381 int64_t seconds;
382 };
383
384 static const TimeframeUnitEntry entries[] = {
385 {"sec", 1},
386 {"second", 1},
387 {"seconds", 1},
388 {"min", SEC_PER_MIN},
389 {"minute", SEC_PER_MIN},
390 {"minutes", SEC_PER_MIN},
391 {"hr", SEC_PER_HOUR},
392 {"hour", SEC_PER_HOUR},
393 {"hours", SEC_PER_HOUR},
394 {"day", SEC_PER_DAY},
395 {"days", SEC_PER_DAY},
396 {"week", SEC_PER_DAY * 7},
397 {"weeks", SEC_PER_DAY * 7},
398 {"month", SEC_PER_DAY * 30},
399 {"months", SEC_PER_DAY * 30},
400 {"quarter", SEC_PER_DAY * 90},
401 {"quarters", SEC_PER_DAY * 90},
402 {"year", SEC_PER_YEAR},
403 {"years", SEC_PER_YEAR}
404 };
405
406 for (std::size_t i = 0; i < sizeof(entries) / sizeof(entries[0]); ++i) {
407 if (ascii_iequals(data, length, entries[i].name)) {
408 unit_seconds = entries[i].seconds;
409 return true;
410 }
411 }
412
413 return false;
414 }
415
417 inline bool try_parse_timeframe_seconds(const char* data, std::size_t length, ts_t& seconds) noexcept {
418 seconds = 0;
419 if (data == nullptr || length == 0) {
420 return false;
421 }
422
423 const char* begin = data;
424 const char* end = data + length;
425 while (begin < end && is_ascii_space(*begin)) {
426 ++begin;
427 }
428 while (end > begin && is_ascii_space(*(end - 1))) {
429 --end;
430 }
431
432 if (begin == end) {
433 return false;
434 }
435
436 int64_t multiplier = 1;
437 int64_t unit_seconds = 0;
438 int64_t result = 0;
439
440 if (is_ascii_digit(*begin)) {
441 const char* cursor = begin;
442 while (cursor < end && is_ascii_digit(*cursor)) {
443 ++cursor;
444 }
445
446 if (!try_parse_positive_int64(begin, static_cast<std::size_t>(cursor - begin), multiplier)) {
447 return false;
448 }
449
450 while (cursor < end && is_ascii_space(*cursor)) {
451 ++cursor;
452 }
453 if (cursor == end) {
454 return false;
455 }
456
457 for (const char* p = cursor; p < end; ++p) {
458 if (!is_ascii_alpha(*p)) {
459 return false;
460 }
461 }
462
463 if (!try_get_timeframe_unit_seconds_word(cursor, static_cast<std::size_t>(end - cursor), unit_seconds)) {
464 return false;
465 }
466
467 if (!try_multiply_positive_int64(multiplier, unit_seconds, result)) {
468 return false;
469 }
470
471 seconds = static_cast<ts_t>(result);
472 return true;
473 }
474
475 if (!is_ascii_alpha(*begin)) {
476 return false;
477 }
478
479 const char* cursor = begin;
480 while (cursor < end && is_ascii_alpha(*cursor)) {
481 ++cursor;
482 }
483
484 if (cursor == end) {
485 if (!try_get_timeframe_unit_seconds_word(begin, static_cast<std::size_t>(end - begin), unit_seconds)) {
486 return false;
487 }
488
489 seconds = static_cast<ts_t>(unit_seconds);
490 return true;
491 }
492
493 if (is_ascii_space(*cursor)) {
494 return false;
495 }
496
497 for (const char* p = cursor; p < end; ++p) {
498 if (!is_ascii_digit(*p)) {
499 return false;
500 }
501 }
502
503 if (!try_get_timeframe_unit_seconds_compact(begin, static_cast<std::size_t>(cursor - begin), unit_seconds)) {
504 return false;
505 }
506 if (!try_parse_positive_int64(cursor, static_cast<std::size_t>(end - cursor), multiplier)) {
507 return false;
508 }
509 if (!try_multiply_positive_int64(multiplier, unit_seconds, result)) {
510 return false;
511 }
512
513 seconds = static_cast<ts_t>(result);
514 return true;
515 }
516
518 TIME_SHIELD_CONSTEXPR inline void skip_spaces(const char*& p, const char* end) noexcept {
519 while (p < end && is_ascii_space(*p)) {
520 ++p;
521 }
522 }
523
526 TIME_SHIELD_CONSTEXPR inline bool parse_2digits(const char*& p, const char* end, int& out) noexcept {
527 if (end - p < 2) {
528 return false;
529 }
530 const char a = p[0];
531 const char b = p[1];
532 if (!is_ascii_digit(a) || !is_ascii_digit(b)) {
533 return false;
534 }
535 out = (a - '0') * 10 + (b - '0');
536 p += 2;
537 return true;
538 }
539
542 TIME_SHIELD_CONSTEXPR inline bool parse_4digits_year(const char*& p, const char* end, year_t& out) noexcept {
543 if (end - p < 4) {
544 return false;
545 }
546 const char a = p[0], b = p[1], c = p[2], d = p[3];
547 if (!is_ascii_digit(a) || !is_ascii_digit(b) || !is_ascii_digit(c) || !is_ascii_digit(d)) {
548 return false;
549 }
550 const int v = (a - '0') * 1000 + (b - '0') * 100 + (c - '0') * 10 + (d - '0');
551 out = static_cast<year_t>(v);
552 p += 4;
553 return true;
554 }
555
559 TIME_SHIELD_CONSTEXPR inline bool parse_fraction_to_ms(const char*& p, const char* end, int& ms_out) noexcept {
560 if (p >= end || !is_ascii_digit(*p)) {
561 return false;
562 }
563
564 int ms = 0;
565 int digits = 0;
566
567 while (p < end && is_ascii_digit(*p)) {
568 if (digits >= 3) {
569 return false;
570 }
571 ms = ms * 10 + (*p - '0');
572 ++digits;
573 ++p;
574 }
575
576 if (digits == 1) {
577 ms *= 100;
578 } else if (digits == 2) {
579 ms *= 10;
580 }
581
582 ms_out = ms;
583 return true;
584 }
585
586 } // namespace detail
587
588//------------------------------------------------------------------------------
589// Month helpers (public)
590//------------------------------------------------------------------------------
591
592// Canonical API (recommended):
593// - parse_month(...) / try_parse_month(...): return month index as int [1..12]
594// - parse_month_enum(...) / try_parse_month_enum(...): return month as enum Month (or any integral/enum T)
595
600 inline bool try_parse_month(const std::string& month, int& value) {
601 return detail::try_parse_month_index(month, value);
602 }
603
608 inline int parse_month(const std::string& month) {
609 return detail::parse_month_index(month);
610 }
611
612#if __cplusplus >= 201703L
617 inline bool try_parse_month(std::string_view month, int& value) {
618 return detail::try_parse_month_index(month, value);
619 }
620
625 inline int parse_month(std::string_view month) {
626 return detail::parse_month_index(month);
627 }
628#endif
629
630// Canonical: parse month -> enum Month (or any T)
631
637 template<class T = Month>
638 inline T parse_month_enum(const std::string& month) {
639 return static_cast<T>(detail::parse_month_index(month));
640 }
641
647 template<class T = Month>
648 inline bool try_parse_month_enum(const std::string& month, T& value) {
649 int idx = 0;
650 if (!detail::try_parse_month_index(month, idx)) return false;
651 value = static_cast<T>(idx);
652 return true;
653 }
654
655#if __cplusplus >= 201703L
661 template<class T = Month>
662 inline T parse_month_enum(std::string_view month) {
663 return static_cast<T>(detail::parse_month_index(month));
664 }
665
671 template<class T = Month>
672 inline bool try_parse_month_enum(std::string_view month, T& value) {
673 int idx = 0;
674 if (!detail::try_parse_month_index(month, idx)) return false;
675 value = static_cast<T>(idx);
676 return true;
677 }
678#endif
679
680// Index aliases (int)
681
686 inline bool try_get_month_index(const std::string& month, int& value) {
687 return try_parse_month(month, value);
688 }
689
694 inline int get_month_index(const std::string& month) {
695 return parse_month(month);
696 }
697
702 inline Month get_month_index_enum(const std::string& month) {
703 return static_cast<Month>(detail::parse_month_index(month));
704 }
705
706#if __cplusplus >= 201703L
708 inline bool try_get_month_index(std::string_view month, int& value) {
709 return try_parse_month(month, value);
710 }
711
713 inline int get_month_index(std::string_view month) {
714 return parse_month(month);
715 }
716
718 inline Month get_month_index_enum(std::string_view month) {
719 return static_cast<Month>(detail::parse_month_index(month));
720 }
721#endif
722
723// Month number aliases (T)
724
730 template<class T = Month>
731 inline T get_month_number(const std::string& month) {
732 return parse_month_enum<T>(month);
733 }
734
736 template<class T = Month>
737 inline T month_of_year(const std::string& month) {
738 return get_month_number<T>(month);
739 }
740
746 template<class T = Month>
747 inline bool try_get_month_number(const std::string& month, T& value) {
748 return try_parse_month_enum<T>(month, value);
749 }
750
752 template<class T = Month>
753 inline bool get_month_number(const std::string& month, T& value) {
754 return try_get_month_number<T>(month, value);
755 }
756
758 template<class T = Month>
759 inline bool month_of_year(const std::string& month, T& value) {
760 return try_get_month_number<T>(month, value);
761 }
762
763#if __cplusplus >= 201703L
765 template<class T = Month>
766 inline T get_month_number(std::string_view month) {
767 return parse_month_enum<T>(month);
768 }
769
771 template<class T = Month>
772 inline T month_of_year(std::string_view month) {
773 return get_month_number<T>(month);
774 }
775
777 template<class T = Month>
778 inline bool try_get_month_number(std::string_view month, T& value) {
779 return try_parse_month_enum<T>(month, value);
780 }
781
783 template<class T = Month>
784 inline bool get_month_number(std::string_view month, T& value) {
785 return try_get_month_number<T>(month, value);
786 }
787
789 template<class T = Month>
790 inline bool month_of_year(std::string_view month, T& value) {
791 return try_get_month_number<T>(month, value);
792 }
793#endif
794
795// const char* overloads to avoid ambiguity with string vs string_view for literals
796
802 template<class T = Month>
803 inline T get_month_number(const char* month) {
804#if __cplusplus >= 201703L
805 return get_month_number<T>(std::string_view(month));
806#else
807 return get_month_number<T>(std::string(month));
808#endif
809 }
810
816 template<class T = Month>
817 inline bool try_get_month_number(const char* month, T& value) {
818#if __cplusplus >= 201703L
819 return try_get_month_number<T>(std::string_view(month), value);
820#else
821 return try_get_month_number<T>(std::string(month), value);
822#endif
823 }
824
826 template<class T = Month>
827 inline T month_of_year(const char* month) {
828 return get_month_number<T>(month);
829 }
830
832 template<class T = Month>
833 inline bool get_month_number(const char* month, T& value) {
834 return try_get_month_number<T>(month, value);
835 }
836
838 template<class T = Month>
839 inline bool month_of_year(const char* month, T& value) {
840 return try_get_month_number<T>(month, value);
841 }
842
843//------------------------------------------------------------------------------
844// Time zone parsing (C-style, high performance)
845//------------------------------------------------------------------------------
846
861 inline bool parse_time_zone(const char* data, std::size_t length, TimeZoneStruct& tz) noexcept {
862 if (!data) {
863 return false;
864 }
865
866 if (length == 0) {
867 tz.hour = 0;
868 tz.min = 0;
869 tz.is_positive = true;
870 return true;
871 }
872
873 if (length == 1 && (data[0] == 'Z' || data[0] == 'z')) {
874 tz.hour = 0;
875 tz.min = 0;
876 tz.is_positive = true;
877 return true;
878 }
879
880 if (length != 6) {
881 return false;
882 }
883
884 const char sign = data[0];
885 if (sign != '+' && sign != '-') {
886 return false;
887 }
888 if (data[3] != ':') {
889 return false;
890 }
891 if (!detail::is_ascii_digit(data[1]) || !detail::is_ascii_digit(data[2]) ||
892 !detail::is_ascii_digit(data[4]) || !detail::is_ascii_digit(data[5])) {
893 return false;
894 }
895
896 tz.is_positive = (sign == '+');
897 tz.hour = (data[1] - '0') * 10 + (data[2] - '0');
898 tz.min = (data[4] - '0') * 10 + (data[5] - '0');
899
900 return is_valid_time_zone(tz);
901 }
902
905 inline bool parse_time_zone(const std::string& tz_str, TimeZoneStruct& tz) noexcept {
906 return parse_time_zone(tz_str.c_str(), tz_str.size(), tz);
907 }
908
910 inline bool parse_tz(const std::string& tz_str, TimeZoneStruct& tz) noexcept {
911 return parse_time_zone(tz_str, tz);
912 }
913
915 inline bool parse_tz(const char* data, std::size_t length, TimeZoneStruct& tz) noexcept {
916 return parse_time_zone(data, length, tz);
917 }
918
925 inline bool parse_time_zone_name(const char* data, std::size_t length, TimeZone& zone) noexcept {
926 if (!data) {
927 zone = UNKNOWN;
928 return false;
929 }
930
931 std::size_t begin = 0;
932 std::size_t end = length;
933 while (begin < end && std::isspace(static_cast<unsigned char>(data[begin])) != 0) {
934 ++begin;
935 }
936 while (end > begin && std::isspace(static_cast<unsigned char>(data[end - 1])) != 0) {
937 --end;
938 }
939 return detail::try_parse_time_zone_name_token(data + begin, end - begin, zone);
940 }
941
944 inline bool parse_time_zone_name(const std::string& value, TimeZone& zone) noexcept {
945 return parse_time_zone_name(value.c_str(), value.size(), zone);
946 }
947
948#if __cplusplus >= 201703L
951 inline bool parse_time_zone_name(std::string_view value, TimeZone& zone) noexcept {
952 return parse_time_zone_name(value.data(), value.size(), zone);
953 }
954#endif
955
958 inline bool parse_time_zone_name(const char* value, TimeZone& zone) noexcept {
959 if (value == nullptr) {
960 zone = UNKNOWN;
961 return false;
962 }
963 return parse_time_zone_name(value, std::strlen(value), zone);
964 }
965
967 inline bool parse_tz_name(const char* data, std::size_t length, TimeZone& zone) noexcept {
968 return parse_time_zone_name(data, length, zone);
969 }
970
972 inline bool parse_tz_name(const std::string& value, TimeZone& zone) noexcept {
973 return parse_time_zone_name(value, zone);
974 }
975
976#if __cplusplus >= 201703L
978 inline bool parse_tz_name(std::string_view value, TimeZone& zone) noexcept {
979 return parse_time_zone_name(value, zone);
980 }
981#endif
982
984 inline bool parse_tz_name(const char* value, TimeZone& zone) noexcept {
985 return parse_time_zone_name(value, zone);
986 }
987
988//------------------------------------------------------------------------------
989// ISO8601 parsing (C-style, no regex, no allocations)
990//------------------------------------------------------------------------------
991
1011 inline bool parse_iso8601(const char* input, std::size_t length,
1012 DateTimeStruct& dt, TimeZoneStruct& tz) noexcept {
1013 if (!input) {
1014 return false;
1015 }
1016
1017 const char* p = input;
1018 const char* end = input + length;
1019
1020 detail::skip_spaces(p, end);
1021
1023 tz = create_time_zone_struct(0, 0);
1024 tz.is_positive = true;
1025
1026 const char* const date_start = p;
1027 const char* date_end = p;
1028 while (date_end < end && *date_end != 'T' && *date_end != 't' && !detail::is_ascii_space(*date_end)) {
1029 ++date_end;
1030 }
1031
1032 bool parsed_iso_week_date = false;
1033 if (date_end > date_start) {
1034 IsoWeekDateStruct iso_date{};
1035 if (parse_iso_week_date(date_start, static_cast<std::size_t>(date_end - date_start), iso_date)) {
1036 const DateStruct calendar_date = iso_week_date_to_date(iso_date);
1037 dt.year = calendar_date.year;
1038 dt.mon = calendar_date.mon;
1039 dt.day = calendar_date.day;
1040 p = date_end;
1041 parsed_iso_week_date = true;
1042 }
1043 }
1044
1045 if (!parsed_iso_week_date) {
1046 // ---- Date: YYYY<sep>MM<sep>DD
1047 if (!detail::parse_4digits_year(p, end, dt.year)) {
1048 return false;
1049 }
1050 if (p >= end) {
1051 return false;
1052 }
1053 const char sep1 = *p;
1054 if (sep1 != '-' && sep1 != '/' && sep1 != '.') {
1055 return false;
1056 }
1057 ++p;
1058
1059 if (!detail::parse_2digits(p, end, dt.mon)) {
1060 return false;
1061 }
1062 if (p >= end) {
1063 return false;
1064 }
1065 const char sep2 = *p;
1066 if (sep2 != '-' && sep2 != '/' && sep2 != '.') {
1067 return false;
1068 }
1069 ++p;
1070
1071 if (!detail::parse_2digits(p, end, dt.day)) {
1072 return false;
1073 }
1074 }
1075
1076 if (!is_valid_date(dt.year, dt.mon, dt.day)) {
1077 return false;
1078 }
1079
1080 // Date-only?
1081 {
1082 const char* q = p;
1083 detail::skip_spaces(q, end);
1084 if (q == end) {
1085 // dt already has time=0 ms=0
1086 return is_valid_date_time(dt);
1087 }
1088 }
1089
1090 // ---- Date/time separator: 'T' or whitespace
1091 if (p >= end) {
1092 return false;
1093 }
1094
1095 if (*p == 'T' || *p == 't') {
1096 ++p;
1097 } else
1098 if (detail::is_ascii_space(*p)) {
1099 // allow one or more spaces
1100 detail::skip_spaces(p, end);
1101 } else {
1102 return false;
1103 }
1104
1105 // ---- Time: hh:mm[:ss][.frac]
1106 if (!detail::parse_2digits(p, end, dt.hour)) {
1107 return false;
1108 }
1109 if (p >= end || *p != ':') {
1110 return false;
1111 }
1112 ++p;
1113
1114 if (!detail::parse_2digits(p, end, dt.min)) {
1115 return false;
1116 }
1117
1118 dt.sec = 0;
1119 dt.ms = 0;
1120 bool has_seconds = false;
1121
1122 // Optional :ss
1123 if (p < end && *p == ':') {
1124 ++p;
1125 if (!detail::parse_2digits(p, end, dt.sec)) {
1126 return false;
1127 }
1128 has_seconds = true;
1129 }
1130
1131 // Optional .fraction (allowed only if we had seconds in original regex,
1132 // but we accept it when seconds are present; for hh:mm (no seconds) we keep it strict).
1133 if (p < end && *p == '.') {
1134 // require seconds field to exist (avoid accepting YYYY-MM-DDThh:mm.xxx)
1135 if (!has_seconds) {
1136 // Ambiguous: could be "hh:mm.fff" which is not in your original formats.
1137 // Keep strict to preserve behavior.
1138 return false;
1139 }
1140
1141 ++p;
1142 int ms = 0;
1143 if (!detail::parse_fraction_to_ms(p, end, ms)) {
1144 return false;
1145 }
1146 dt.ms = ms;
1147 }
1148
1149 // ---- Optional timezone: [spaces] (Z | +/-HH:MM)
1150 detail::skip_spaces(p, end);
1151
1152 if (p < end) {
1153 if (*p == 'Z' || *p == 'z') {
1154 tz.hour = 0;
1155 tz.min = 0;
1156 tz.is_positive = true;
1157 ++p;
1158 } else if (*p == '+' || *p == '-') {
1159 // need 6 chars
1160 if (static_cast<std::size_t>(end - p) < 6) {
1161 return false;
1162 }
1163 if (!parse_time_zone(p, 6, tz)) {
1164 return false;
1165 }
1166 p += 6;
1167 }
1168 }
1169
1170 detail::skip_spaces(p, end);
1171 if (p != end) {
1172 return false;
1173 }
1174
1175 return is_valid_date_time(dt);
1176 }
1177
1180 inline bool parse_iso8601(const std::string& input, DateTimeStruct& dt, TimeZoneStruct& tz) noexcept {
1181 return parse_iso8601(input.c_str(), input.size(), dt, tz);
1182 }
1183
1186 inline bool parse_iso8601(const char* input, DateTimeStruct& dt, TimeZoneStruct& tz) noexcept {
1187 if (input == nullptr) {
1188 return false;
1189 }
1190 return parse_iso8601(input, std::strlen(input), dt, tz);
1191 }
1192
1193# if __cplusplus >= 201703L
1196 inline bool parse_iso8601(std::string_view input, DateTimeStruct& dt, TimeZoneStruct& tz) noexcept {
1197 return parse_iso8601(input.data(), input.size(), dt, tz);
1198 }
1199# endif
1200
1201//------------------------------------------------------------------------------
1202// ISO8601 -> timestamps
1203//------------------------------------------------------------------------------
1204
1209 inline bool str_to_ts(const std::string& str, ts_t& ts) {
1210 DateTimeStruct dt;
1211 TimeZoneStruct tz;
1212 if (!parse_iso8601(str, dt, tz)) return false;
1213 try {
1214 ts = dt_to_timestamp(dt) - to_offset(tz);
1215 return true;
1216 } catch (...) {}
1217 return false;
1218 }
1219
1225 inline bool str_to_ts(const char* data, std::size_t length, ts_t& ts) {
1226 if (!data || length == 0) {
1227 ts = 0;
1228 return false;
1229 }
1230 DateTimeStruct dt;
1231 TimeZoneStruct tz;
1232 if (!parse_iso8601(data, length, dt, tz)) return false;
1233 try {
1234 ts = dt_to_timestamp(dt) - to_offset(tz);
1235 return true;
1236 } catch (...) {}
1237 return false;
1238 }
1239
1244 inline bool str_to_ts_ms(const std::string& str, ts_ms_t& ts) {
1245 DateTimeStruct dt;
1246 TimeZoneStruct tz;
1247 if (!parse_iso8601(str, dt, tz)) return false;
1248 try {
1249 ts = static_cast<ts_ms_t>(dt_to_timestamp_ms(dt)) - sec_to_ms(to_offset(tz));
1250 return true;
1251 } catch (...) {}
1252 return false;
1253 }
1254
1260 inline bool str_to_ts_ms(const char* data, std::size_t length, ts_ms_t& ts) {
1261 if (!data || length == 0) {
1262 ts = 0;
1263 return false;
1264 }
1265 DateTimeStruct dt;
1266 TimeZoneStruct tz;
1267 if (!parse_iso8601(data, length, dt, tz)) return false;
1268 try {
1269 ts = static_cast<ts_ms_t>(dt_to_timestamp_ms(dt)) - sec_to_ms(to_offset(tz));
1270 return true;
1271 } catch (...) {}
1272 return false;
1273 }
1274
1279 inline bool str_to_fts(const std::string& str, fts_t& ts) {
1280 DateTimeStruct dt;
1281 TimeZoneStruct tz;
1282 if (!parse_iso8601(str, dt, tz)) return false;
1283 try {
1284 ts = dt_to_ftimestamp(dt) - static_cast<fts_t>(to_offset(tz));
1285 return true;
1286 } catch (...) {}
1287 return false;
1288 }
1289
1295 inline bool str_to_fts(const char* data, std::size_t length, fts_t& ts) {
1296 if (!data || length == 0) {
1297 ts = 0;
1298 return false;
1299 }
1300 DateTimeStruct dt;
1301 TimeZoneStruct tz;
1302 if (!parse_iso8601(data, length, dt, tz)) return false;
1303 try {
1304 ts = dt_to_ftimestamp(dt) - static_cast<fts_t>(to_offset(tz));
1305 return true;
1306 } catch (...) {}
1307 return false;
1308 }
1309
1310//------------------------------------------------------------------------------
1311// Convenience string -> predicates (workdays)
1312//------------------------------------------------------------------------------
1313
1315 inline bool is_workday(const std::string& str) {
1316 ts_t ts = 0;
1317 if (!str_to_ts(str, ts)) return false;
1318 return is_workday(ts);
1319 }
1320
1322 inline bool is_workday_ms(const std::string& str) {
1323 ts_ms_t ts = 0;
1324 if (!str_to_ts_ms(str, ts)) return false;
1325 return is_workday_ms(ts);
1326 }
1327
1330 inline bool workday(const std::string& str) {
1331 return is_workday(str);
1332 }
1333
1336 inline bool workday_ms(const std::string& str) {
1337 return is_workday_ms(str);
1338 }
1339
1343 inline bool is_first_workday_of_month(const std::string& str) {
1344 ts_t ts = 0;
1345 if (!str_to_ts(str, ts)) return false;
1347 }
1348
1352 inline bool is_first_workday_of_month_ms(const std::string& str) {
1353 ts_ms_t ts = 0;
1354 if (!str_to_ts_ms(str, ts)) return false;
1356 }
1357
1361 inline bool is_last_workday_of_month(const std::string& str) {
1362 ts_t ts = 0;
1363 if (!str_to_ts(str, ts)) return false;
1365 }
1366
1370 inline bool is_last_workday_of_month_ms(const std::string& str) {
1371 ts_ms_t ts = 0;
1372 if (!str_to_ts_ms(str, ts)) return false;
1374 }
1375
1380 inline bool is_within_first_workdays_of_month(const std::string& str, int count) {
1381 ts_t ts = 0;
1382 if (!str_to_ts(str, ts)) return false;
1384 }
1385
1390 inline bool is_within_first_workdays_of_month_ms(const std::string& str, int count) {
1391 ts_ms_t ts = 0;
1392 if (!str_to_ts_ms(str, ts)) return false;
1394 }
1395
1400 inline bool is_within_last_workdays_of_month(const std::string& str, int count) {
1401 ts_t ts = 0;
1402 if (!str_to_ts(str, ts)) return false;
1403 return is_within_last_workdays_of_month(ts, count);
1404 }
1405
1410 inline bool is_within_last_workdays_of_month_ms(const std::string& str, int count) {
1411 ts_ms_t ts = 0;
1412 if (!str_to_ts_ms(str, ts)) return false;
1414 }
1415
1416//------------------------------------------------------------------------------
1417// Convenience: C-string wrappers (non-throwing, ambiguous on failure)
1418//------------------------------------------------------------------------------
1419
1424 inline ts_t ts(const char* str) {
1425 ts_t out = 0;
1426 str_to_ts(str ? std::string(str) : std::string(), out);
1427 return out;
1428 }
1429
1436 inline ts_t ts(const char* data, std::size_t length) {
1437 ts_t out = 0;
1438 if (!str_to_ts(data, length, out)) {
1439 return 0;
1440 }
1441 return out;
1442 }
1443
1448 inline ts_ms_t ts_ms(const char* str) {
1449 ts_ms_t out = 0;
1450 str_to_ts_ms(str ? std::string(str) : std::string(), out);
1451 return out;
1452 }
1453
1459 inline ts_ms_t ts_ms(const char* data, std::size_t length) {
1460 ts_ms_t out = 0;
1461 if (!str_to_ts_ms(data, length, out)) {
1462 return 0;
1463 }
1464 return out;
1465 }
1466
1471 inline fts_t fts(const char* str) {
1472 fts_t out = 0;
1473 str_to_fts(str ? std::string(str) : std::string(), out);
1474 return out;
1475 }
1476
1482 inline fts_t fts(const char* data, std::size_t length) {
1483 fts_t out = 0;
1484 if (!str_to_fts(data, length, out)) {
1485 return 0.0;
1486 }
1487 return out;
1488 }
1489
1490//------------------------------------------------------------------------------
1491
1497 inline ts_t ts(const std::string& str) {
1498 ts_t ts = 0;
1499 str_to_ts(str, ts);
1500 return ts;
1501 }
1502
1508 inline ts_ms_t ts_ms(const std::string& str) {
1509 ts_ms_t ts = 0;
1510 str_to_ts_ms(str, ts);
1511 return ts;
1512 }
1513
1519 inline fts_t fts(const std::string& str) {
1520 fts_t ts = 0;
1521 str_to_fts(str, ts);
1522 return ts;
1523 }
1524
1525//------------------------------------------------------------------------------
1526
1531 inline bool str_to_timeframe_sec(const std::string& str, ts_t& seconds) noexcept {
1532 return detail::try_parse_timeframe_seconds(str.c_str(), str.size(), seconds);
1533 }
1534
1539 inline bool str_to_timeframe_sec(const char* str, ts_t& seconds) noexcept {
1540 if (str == nullptr) {
1541 seconds = 0;
1542 return false;
1543 }
1544 return detail::try_parse_timeframe_seconds(str, std::strlen(str), seconds);
1545 }
1546
1547# if __cplusplus >= 201703L
1552 inline bool str_to_timeframe_sec(std::string_view str, ts_t& seconds) noexcept {
1553 return detail::try_parse_timeframe_seconds(str.data(), str.size(), seconds);
1554 }
1555# endif
1556
1561 inline bool str_to_timeframe_ms(const std::string& str, ts_ms_t& milliseconds) noexcept {
1562 ts_t seconds = 0;
1563 if (!detail::try_parse_timeframe_seconds(str.c_str(), str.size(), seconds)) {
1564 milliseconds = 0;
1565 return false;
1566 }
1567
1568 int64_t milliseconds_value = 0;
1569 if (!detail::try_multiply_positive_int64(seconds, MS_PER_SEC, milliseconds_value)) {
1570 milliseconds = 0;
1571 return false;
1572 }
1573
1574 milliseconds = static_cast<ts_ms_t>(milliseconds_value);
1575 return true;
1576 }
1577
1582 inline bool str_to_timeframe_ms(const char* str, ts_ms_t& milliseconds) noexcept {
1583 if (str == nullptr) {
1584 milliseconds = 0;
1585 return false;
1586 }
1587
1588 ts_t seconds = 0;
1589 if (!detail::try_parse_timeframe_seconds(str, std::strlen(str), seconds)) {
1590 milliseconds = 0;
1591 return false;
1592 }
1593
1594 int64_t milliseconds_value = 0;
1595 if (!detail::try_multiply_positive_int64(seconds, MS_PER_SEC, milliseconds_value)) {
1596 milliseconds = 0;
1597 return false;
1598 }
1599
1600 milliseconds = static_cast<ts_ms_t>(milliseconds_value);
1601 return true;
1602 }
1603
1604# if __cplusplus >= 201703L
1609 inline bool str_to_timeframe_ms(std::string_view str, ts_ms_t& milliseconds) noexcept {
1610 ts_t seconds = 0;
1611 if (!detail::try_parse_timeframe_seconds(str.data(), str.size(), seconds)) {
1612 milliseconds = 0;
1613 return false;
1614 }
1615
1616 int64_t milliseconds_value = 0;
1617 if (!detail::try_multiply_positive_int64(seconds, MS_PER_SEC, milliseconds_value)) {
1618 milliseconds = 0;
1619 return false;
1620 }
1621
1622 milliseconds = static_cast<ts_ms_t>(milliseconds_value);
1623 return true;
1624 }
1625# endif
1626
1631 inline ts_t timeframe_sec(const std::string& str) noexcept {
1632 ts_t seconds = 0;
1633 str_to_timeframe_sec(str, seconds);
1634 return seconds;
1635 }
1636
1641 inline ts_t timeframe_sec(const char* str) noexcept {
1642 ts_t seconds = 0;
1643 str_to_timeframe_sec(str, seconds);
1644 return seconds;
1645 }
1646
1647# if __cplusplus >= 201703L
1652 inline ts_t timeframe_sec(std::string_view str) noexcept {
1653 ts_t seconds = 0;
1654 str_to_timeframe_sec(str, seconds);
1655 return seconds;
1656 }
1657# endif
1658
1663 inline ts_ms_t timeframe_ms(const std::string& str) noexcept {
1664 ts_ms_t milliseconds = 0;
1665 str_to_timeframe_ms(str, milliseconds);
1666 return milliseconds;
1667 }
1668
1673 inline ts_ms_t timeframe_ms(const char* str) noexcept {
1674 ts_ms_t milliseconds = 0;
1675 str_to_timeframe_ms(str, milliseconds);
1676 return milliseconds;
1677 }
1678
1679# if __cplusplus >= 201703L
1684 inline ts_ms_t timeframe_ms(std::string_view str) noexcept {
1685 ts_ms_t milliseconds = 0;
1686 str_to_timeframe_ms(str, milliseconds);
1687 return milliseconds;
1688 }
1689# endif
1690
1691//------------------------------------------------------------------------------
1692
1704 template<class T = int>
1705 inline bool sec_of_day(const std::string& str, T& sec) {
1706 if (str.empty()) return false;
1707
1708 const char* p = str.c_str();
1709 int parts[3] = {0, 0, 0}; // hour, minute, second
1710 int idx = 0;
1711
1712 while (*p && idx < 3) {
1713 // Parse integer
1714 int value = 0;
1715 bool has_digit = false;
1716
1717 while (*p >= '0' && *p <= '9') {
1718 has_digit = true;
1719 value = value * 10 + (*p - '0');
1720 ++p;
1721 }
1722
1723 if (!has_digit) return false;
1724 parts[idx++] = value;
1725
1726 // Expect colon or end
1727 if (*p == ':') {
1728 ++p;
1729 } else if (*p == '\0') {
1730 break;
1731 } else {
1732 return false; // unexpected character
1733 }
1734 }
1735
1736 if (idx == 0) return false;
1737 if (!is_valid_time(parts[0], parts[1], parts[2])) return false;
1738
1739 sec = static_cast<T>(sec_of_day(parts[0], parts[1], parts[2]));
1740 return true;
1741 }
1742
1753 template<class T = int>
1754 inline T sec_of_day(const std::string& str) {
1755 T value{};
1756 if (sec_of_day(str, value))
1757 return value;
1758 return static_cast<T>(SEC_PER_DAY);
1759 }
1760
1762
1763};
1764
1765#endif // TIME_SHIELD_HEADER_TEXT_TIME_PARSER_HPP_INCLUDED
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 is_first_workday_of_month_ms(ts_ms_t ts_ms) noexcept
Checks whether millisecond timestamp is the first workday of the month.
TIME_SHIELD_CONSTEXPR bool is_within_last_workdays_of_month_ms(ts_ms_t ts_ms, int count) noexcept
Checks if millisecond timestamp falls within the last N workdays of the month.
TIME_SHIELD_CONSTEXPR bool is_within_first_workdays_of_month(year_t year, int month, int day, int count) noexcept
Checks if date falls within the first N workdays of the month.
TIME_SHIELD_CONSTEXPR bool workday(ts_t ts) noexcept
Alias for is_workday(ts_t).
TIME_SHIELD_CONSTEXPR bool is_within_first_workdays_of_month_ms(ts_ms_t ts_ms, int count) noexcept
Checks if millisecond timestamp falls within the first N workdays of the month.
TIME_SHIELD_CONSTEXPR bool is_last_workday_of_month(year_t year, int month, int day) noexcept
Checks whether date is the last workday of the month.
DateStruct iso_week_date_to_date(const IsoWeekDateStruct &iso_date)
Convert ISO week date to calendar date.
TIME_SHIELD_CONSTEXPR bool is_first_workday_of_month(year_t year, int month, int day) noexcept
Checks whether date is the first workday of the month.
TIME_SHIELD_CONSTEXPR bool is_last_workday_of_month_ms(ts_ms_t ts_ms) noexcept
Checks whether millisecond timestamp is the last workday of the month.
TIME_SHIELD_CONSTEXPR bool is_within_last_workdays_of_month(year_t year, int month, int day, int count) noexcept
Checks if date falls within the last N workdays of the month.
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 str_to_ts_ms(const std::string &str, ts_ms_t &ts)
Convert an ISO8601 string to a millisecond timestamp (ts_ms_t).
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 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 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 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.
T parse_month_enum(const std::string &month)
Parse month name token into Month enum (throwing).
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:48
int64_t ts_ms_t
Unix timestamp in milliseconds since epoch.
Definition types.hpp:49
double fts_t
Floating-point timestamp (fractional seconds since epoch).
Definition types.hpp:51
int64_t year_t
Year as an integer (e.g., 2024).
Definition types.hpp:40
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.
TIME_SHIELD_CONSTEXPR bool is_workday(ts_t ts) noexcept
Check if a given timestamp corresponds to a workday (Monday to Friday).
TIME_SHIELD_CONSTEXPR bool is_workday_ms(ts_ms_t ts_ms) noexcept
Check if a given timestamp in milliseconds corresponds to a workday (Monday to Friday).
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.
TIME_SHIELD_CONSTEXPR T sec_of_day(ts_t ts=time_shield::ts()) noexcept
Get the second of the day.
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.
Header file for fast parsing with formatter-compatible custom patterns.