3#ifndef _TIME_SHIELD_TIME_PARSER_HPP_INCLUDED
4#define _TIME_SHIELD_TIME_PARSER_HPP_INCLUDED
34#if __cplusplus >= 201703L
35# include <string_view>
78 while (b < e && std::isspace(
static_cast<unsigned char>(s[b])) != 0) ++b;
79 while (e > b && std::isspace(
static_cast<unsigned char>(s[e - 1])) != 0) --e;
80 return s.substr(b, e - b);
83# if __cplusplus >= 201703L
88 while (b < e && std::isspace(
static_cast<unsigned char>(v[b])) != 0) ++b;
89 while (e > b && std::isspace(
static_cast<unsigned char>(v[e - 1])) != 0) --e;
90 return v.substr(b, e - b);
99 if (output.empty())
return;
101 const auto& facet = std::use_facet<std::ctype<char>>(std::locale());
102 std::transform(output.begin(), output.end(), output.begin(),
103 [&facet](
char ch) { return facet.tolower(ch); });
106# if __cplusplus >= 201703L
112 output.assign(month.begin(), month.end());
113 if (output.empty())
return;
115 const auto& facet = std::use_facet<std::ctype<char>>(std::locale());
116 std::transform(output.begin(), output.end(), output.begin(),
117 [&facet](
char ch) { return facet.tolower(ch); });
126 if (month.empty())
return false;
128 std::string month_copy;
130 if (month_copy.empty())
return false;
132 static const std::array<const char*, MONTHS_PER_YEAR> short_names = {
133 "jan",
"feb",
"mar",
"apr",
"may",
"jun",
134 "jul",
"aug",
"sep",
"oct",
"nov",
"dec"
136 static const std::array<const char*, MONTHS_PER_YEAR> full_names = {
137 "january",
"february",
"march",
"april",
"may",
"june",
138 "july",
"august",
"september",
"october",
"november",
"december"
141 for (std::size_t i = 0; i < short_names.size(); ++i) {
142 if (month_copy == short_names[i] || month_copy == full_names[i]) {
143 value =
static_cast<int>(i) + 1;
151# if __cplusplus >= 201703L
157 if (month.empty())
return false;
159 std::string month_copy;
161 if (month_copy.empty())
return false;
163 static const std::array<const char*, MONTHS_PER_YEAR> short_names = {
164 "jan",
"feb",
"mar",
"apr",
"may",
"jun",
165 "jul",
"aug",
"sep",
"oct",
"nov",
"dec"
167 static const std::array<const char*, MONTHS_PER_YEAR> full_names = {
168 "january",
"february",
"march",
"april",
"may",
"june",
169 "july",
"august",
"september",
"october",
"november",
"december"
172 for (std::size_t i = 0; i < short_names.size(); ++i) {
173 if (month_copy == short_names[i] || month_copy == full_names[i]) {
174 value =
static_cast<int>(i) + 1;
190 throw std::invalid_argument(
"Invalid month name");
195# if __cplusplus >= 201703L
203 throw std::invalid_argument(
"Invalid month name");
215 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
'\f' || c ==
'\v';
220 return c >=
'0' && c <=
'9';
224 TIME_SHIELD_CONSTEXPR
inline void skip_spaces(
const char*& p,
const char* end)
noexcept {
232 TIME_SHIELD_CONSTEXPR
inline bool parse_2digits(
const char*& p,
const char* end,
int& out)
noexcept {
241 out = (a -
'0') * 10 + (b -
'0');
252 const char a = p[0], b = p[1], c = p[2], d = p[3];
256 const int v = (a -
'0') * 1000 + (b -
'0') * 100 + (c -
'0') * 10 + (d -
'0');
257 out =
static_cast<year_t>(v);
265 TIME_SHIELD_CONSTEXPR
inline bool parse_fraction_to_ms(
const char*& p,
const char* end,
int& ms_out)
noexcept {
277 ms = ms * 10 + (*p -
'0');
284 }
else if (digits == 2) {
318#if __cplusplus >= 201703L
343 template<
class T = Month>
353 template<
class T = Month>
357 value =
static_cast<T
>(idx);
361#if __cplusplus >= 201703L
367 template<
class T = Month>
377 template<
class T = Month>
381 value =
static_cast<T
>(idx);
412#if __cplusplus >= 201703L
436 template<
class T = Month>
442 template<
class T = Month>
452 template<
class T = Month>
458 template<
class T = Month>
464 template<
class T = Month>
469#if __cplusplus >= 201703L
471 template<
class T = Month>
477 template<
class T = Month>
483 template<
class T = Month>
489 template<
class T = Month>
495 template<
class T = Month>
508 template<
class T = Month>
510#if __cplusplus >= 201703L
522 template<
class T = Month>
524#if __cplusplus >= 201703L
532 template<
class T = Month>
538 template<
class T = Month>
544 template<
class T = Month>
570 tz.is_positive =
true;
574 if (length == 1 && (data[0] ==
'Z' || data[0] ==
'z')) {
577 tz.is_positive =
true;
585 const char sign = data[0];
586 if (sign !=
'+' && sign !=
'-') {
589 if (data[3] !=
':') {
597 tz.is_positive = (sign ==
'+');
598 tz.hour = (data[1] -
'0') * 10 + (data[2] -
'0');
599 tz.min = (data[4] -
'0') * 10 + (data[5] -
'0');
646 const char* p = input;
647 const char* end = input + length;
653 tz.is_positive =
true;
655 const char*
const date_start = p;
656 const char* date_end = p;
661 bool parsed_iso_week_date =
false;
662 if (date_end > date_start) {
664 if (
parse_iso_week_date(date_start,
static_cast<std::size_t
>(date_end - date_start), iso_date)) {
666 dt.year = calendar_date.
year;
667 dt.mon = calendar_date.
mon;
668 dt.day = calendar_date.
day;
670 parsed_iso_week_date =
true;
674 if (!parsed_iso_week_date) {
682 const char sep1 = *p;
683 if (sep1 !=
'-' && sep1 !=
'/' && sep1 !=
'.') {
694 const char sep2 = *p;
695 if (sep2 !=
'-' && sep2 !=
'/' && sep2 !=
'.') {
724 if (*p ==
'T' || *p ==
't') {
738 if (p >= end || *p !=
':') {
749 bool has_seconds =
false;
752 if (p < end && *p ==
':') {
762 if (p < end && *p ==
'.') {
782 if (*p ==
'Z' || *p ==
'z') {
785 tz.is_positive =
true;
787 }
else if (*p ==
'+' || *p ==
'-') {
789 if (
static_cast<std::size_t
>(end - p) < 6) {
838 if (!data || length == 0) {
867 if (!data || length == 0) {
895 if (!data || length == 0) {
1018 str_to_ts(str ? std::string(str) : std::string(), out);
1028 inline ts_t ts(
const char* data, std::size_t length) {
1042 str_to_ts_ms(str ? std::string(str) : std::string(), out);
1065 str_to_fts(str ? std::string(str) : std::string(), out);
1074 inline fts_t fts(
const char* data, std::size_t length) {
1130 template<
class T =
int>
1132 if (str.empty())
return false;
1134 const char* p = str.c_str();
1135 int parts[3] = {0, 0, 0};
1138 while (*p && idx < 3) {
1141 bool has_digit =
false;
1143 while (*p >=
'0' && *p <=
'9') {
1145 value = value * 10 + (*p -
'0');
1149 if (!has_digit)
return false;
1150 parts[idx++] = value;
1155 }
else if (*p ==
'\0') {
1162 if (idx == 0)
return false;
1163 if (!
is_valid_time(parts[0], parts[1], parts[2]))
return false;
1165 sec =
static_cast<T
>(
sec_of_day(parts[0], parts[1], parts[2]));
1179 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_DAY
Seconds per day.
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).
constexpr T1 sec_to_ms(T2 ts) noexcept
Converts a timestamp from seconds to milliseconds.
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 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.
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 parse_tz(const std::string &tz_str, TimeZoneStruct &tz) noexcept
Alias for parse_time_zone.
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).
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_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.
TIME_SHIELD_CONSTEXPR ts_t dt_to_timestamp_ms(const T &date_time)
Converts a date-time structure to a timestamp in milliseconds.
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).
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 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.
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.
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).
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.