3#ifndef _TIME_SHIELD_TIME_PARSER_HPP_INCLUDED
4#define _TIME_SHIELD_TIME_PARSER_HPP_INCLUDED
38#if __cplusplus >= 201703L
39# include <string_view>
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);
87# if __cplusplus >= 201703L
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);
103 if (output.empty())
return;
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); });
110# if __cplusplus >= 201703L
116 output.assign(month.begin(), month.end());
117 if (output.empty())
return;
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); });
130 if (month.empty())
return false;
132 std::string month_copy;
134 if (month_copy.empty())
return false;
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"
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"
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;
155# if __cplusplus >= 201703L
161 if (month.empty())
return false;
163 std::string month_copy;
165 if (month_copy.empty())
return false;
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"
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"
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;
194 throw std::invalid_argument(
"Invalid month name");
199# if __cplusplus >= 201703L
207 throw std::invalid_argument(
"Invalid month name");
220 static const std::array<ZoneNameEntry, 25> entries = {{
252 if (data ==
nullptr || length == 0) {
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;
276 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
'\f' || c ==
'\v';
281 return c >=
'0' && c <=
'9';
286 return (c >=
'A' && c <=
'Z') || (c >=
'a' && c <=
'z');
291 return (c >=
'A' && c <=
'Z') ?
static_cast<char>(c -
'A' +
'a') : c;
295 inline bool ascii_iequals(
const char* data, std::size_t length,
const char* literal)
noexcept {
296 if (data ==
nullptr || literal ==
nullptr) {
300 for (std::size_t i = 0; i < length; ++i) {
306 return literal[length] ==
'\0';
312 if (data ==
nullptr || length == 0) {
316 const int64_t max_value = (std::numeric_limits<int64_t>::max)();
317 for (std::size_t i = 0; i < length; ++i) {
322 const int digit = data[i] -
'0';
323 if (value > (max_value - digit) / 10) {
327 value = value * 10 + digit;
335 if (lhs <= 0 || rhs <= 0) {
339 if (lhs > (std::numeric_limits<int64_t>::max)() / rhs) {
350 if (data ==
nullptr || length == 0) {
356 case 's': unit_seconds = 1;
return true;
360 case 'w': unit_seconds =
SEC_PER_DAY * 7;
return true;
361 case 'q': unit_seconds =
SEC_PER_DAY * 90;
return true;
363 default:
return false;
380 if (data ==
nullptr || length == 0) {
384 struct TimeframeUnitEntry {
389 static const TimeframeUnitEntry entries[] = {
411 for (std::size_t i = 0; i <
sizeof(entries) /
sizeof(entries[0]); ++i) {
413 unit_seconds = entries[i].seconds;
424 if (data ==
nullptr || length == 0) {
428 const char* begin = data;
429 const char* end = data + length;
441 int64_t multiplier = 1;
442 int64_t unit_seconds = 0;
446 const char* cursor = begin;
462 for (
const char* p = cursor; p < end; ++p) {
476 seconds =
static_cast<ts_t>(result);
484 const char* cursor = begin;
494 seconds =
static_cast<ts_t>(unit_seconds);
502 for (
const char* p = cursor; p < end; ++p) {
518 seconds =
static_cast<ts_t>(result);
523 TIME_SHIELD_CONSTEXPR
inline void skip_spaces(
const char*& p,
const char* end)
noexcept {
531 TIME_SHIELD_CONSTEXPR
inline bool parse_2digits(
const char*& p,
const char* end,
int& out)
noexcept {
540 out = (a -
'0') * 10 + (b -
'0');
551 const char a = p[0], b = p[1], c = p[2], d = p[3];
555 const int v = (a -
'0') * 1000 + (b -
'0') * 100 + (c -
'0') * 10 + (d -
'0');
556 out =
static_cast<year_t>(v);
564 TIME_SHIELD_CONSTEXPR
inline bool parse_fraction_to_ms(
const char*& p,
const char* end,
int& ms_out)
noexcept {
576 ms = ms * 10 + (*p -
'0');
583 }
else if (digits == 2) {
617#if __cplusplus >= 201703L
642 template<
class T = Month>
652 template<
class T = Month>
656 value =
static_cast<T
>(idx);
660#if __cplusplus >= 201703L
666 template<
class T = Month>
676 template<
class T = Month>
680 value =
static_cast<T
>(idx);
711#if __cplusplus >= 201703L
735 template<
class T = Month>
741 template<
class T = Month>
751 template<
class T = Month>
757 template<
class T = Month>
763 template<
class T = Month>
768#if __cplusplus >= 201703L
770 template<
class T = Month>
776 template<
class T = Month>
782 template<
class T = Month>
788 template<
class T = Month>
794 template<
class T = Month>
807 template<
class T = Month>
809#if __cplusplus >= 201703L
821 template<
class T = Month>
823#if __cplusplus >= 201703L
831 template<
class T = Month>
837 template<
class T = Month>
843 template<
class T = Month>
874 tz.is_positive =
true;
878 if (length == 1 && (data[0] ==
'Z' || data[0] ==
'z')) {
881 tz.is_positive =
true;
889 const char sign = data[0];
890 if (sign !=
'+' && sign !=
'-') {
893 if (data[3] !=
':') {
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');
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) {
941 while (end > begin && std::isspace(
static_cast<unsigned char>(data[end - 1])) != 0) {
953#if __cplusplus >= 201703L
964 if (value ==
nullptr) {
981#if __cplusplus >= 201703L
1022 const char* p = input;
1023 const char* end = input + length;
1029 tz.is_positive =
true;
1031 const char*
const date_start = p;
1032 const char* date_end = p;
1037 bool parsed_iso_week_date =
false;
1038 if (date_end > date_start) {
1040 if (
parse_iso_week_date(date_start,
static_cast<std::size_t
>(date_end - date_start), iso_date)) {
1042 dt.year = calendar_date.
year;
1043 dt.mon = calendar_date.
mon;
1044 dt.day = calendar_date.
day;
1046 parsed_iso_week_date =
true;
1050 if (!parsed_iso_week_date) {
1058 const char sep1 = *p;
1059 if (sep1 !=
'-' && sep1 !=
'/' && sep1 !=
'.') {
1070 const char sep2 = *p;
1071 if (sep2 !=
'-' && sep2 !=
'/' && sep2 !=
'.') {
1100 if (*p ==
'T' || *p ==
't') {
1114 if (p >= end || *p !=
':') {
1125 bool has_seconds =
false;
1128 if (p < end && *p ==
':') {
1138 if (p < end && *p ==
'.') {
1158 if (*p ==
'Z' || *p ==
'z') {
1161 tz.is_positive =
true;
1163 }
else if (*p ==
'+' || *p ==
'-') {
1165 if (
static_cast<std::size_t
>(end - p) < 6) {
1192 if (input ==
nullptr) {
1198# if __cplusplus >= 201703L
1231 if (!data || length == 0) {
1266 if (!data || length == 0) {
1301 if (!data || length == 0) {
1431 str_to_ts(str ? std::string(str) : std::string(), out);
1441 inline ts_t ts(
const char* data, std::size_t length) {
1455 str_to_ts_ms(str ? std::string(str) : std::string(), out);
1478 str_to_fts(str ? std::string(str) : std::string(), out);
1487 inline fts_t fts(
const char* data, std::size_t length) {
1545 if (str ==
nullptr) {
1552# if __cplusplus >= 201703L
1573 int64_t milliseconds_value = 0;
1579 milliseconds =
static_cast<ts_ms_t>(milliseconds_value);
1588 if (str ==
nullptr) {
1599 int64_t milliseconds_value = 0;
1605 milliseconds =
static_cast<ts_ms_t>(milliseconds_value);
1609# if __cplusplus >= 201703L
1621 int64_t milliseconds_value = 0;
1627 milliseconds =
static_cast<ts_ms_t>(milliseconds_value);
1652# if __cplusplus >= 201703L
1671 return milliseconds;
1681 return milliseconds;
1684# if __cplusplus >= 201703L
1692 return milliseconds;
1709 template<
class T =
int>
1711 if (str.empty())
return false;
1713 const char* p = str.c_str();
1714 int parts[3] = {0, 0, 0};
1717 while (*p && idx < 3) {
1720 bool has_digit =
false;
1722 while (*p >=
'0' && *p <=
'9') {
1724 value = value * 10 + (*p -
'0');
1728 if (!has_digit)
return false;
1729 parts[idx++] = value;
1734 }
else if (*p ==
'\0') {
1741 if (idx == 0)
return false;
1742 if (!
is_valid_time(parts[0], parts[1], parts[2]))
return false;
1744 sec =
static_cast<T
>(
sec_of_day(parts[0], parts[1], parts[2]));
1758 template<
class T =
int>
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.
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.
@ KST
Korea Standard Time.
@ EET
Eastern European Time.
@ CEST
Central European Summer Time.
@ WIT
Eastern Indonesia Time.
@ WEST
Western European Summer Time.
@ WITA
Central Indonesia Time.
@ WET
Western European Time.
@ UNKNOWN
Unknown Time Zone.
@ UTC
Coordinated Universal Time.
@ GMT
Greenwich Mean Time.
@ WIB
Western Indonesia Time.
@ EEST
Eastern European Summer Time.
@ CET
Central European Time.
@ IST
India Standard Time.
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.
int64_t ts_ms_t
Unix timestamp in milliseconds since epoch.
double fts_t
Floating-point timestamp (fractional seconds since epoch).
int64_t year_t
Year as an integer (e.g., 2024).
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 for time zone structure and related functions.
Header file with time-related validation functions.