3#ifndef _TIME_SHIELD_TIME_PARSER_HPP_INCLUDED
4#define _TIME_SHIELD_TIME_PARSER_HPP_INCLUDED
36#if __cplusplus >= 201703L
37# include <string_view>
80 while (b < e && std::isspace(
static_cast<unsigned char>(s[b])) != 0) ++b;
81 while (e > b && std::isspace(
static_cast<unsigned char>(s[e - 1])) != 0) --e;
82 return s.substr(b, e - b);
85# if __cplusplus >= 201703L
90 while (b < e && std::isspace(
static_cast<unsigned char>(v[b])) != 0) ++b;
91 while (e > b && std::isspace(
static_cast<unsigned char>(v[e - 1])) != 0) --e;
92 return v.substr(b, e - b);
101 if (output.empty())
return;
103 const auto& facet = std::use_facet<std::ctype<char>>(std::locale());
104 std::transform(output.begin(), output.end(), output.begin(),
105 [&facet](
char ch) { return facet.tolower(ch); });
108# if __cplusplus >= 201703L
114 output.assign(month.begin(), month.end());
115 if (output.empty())
return;
117 const auto& facet = std::use_facet<std::ctype<char>>(std::locale());
118 std::transform(output.begin(), output.end(), output.begin(),
119 [&facet](
char ch) { return facet.tolower(ch); });
128 if (month.empty())
return false;
130 std::string month_copy;
132 if (month_copy.empty())
return false;
134 static const std::array<const char*, MONTHS_PER_YEAR> short_names = {
135 "jan",
"feb",
"mar",
"apr",
"may",
"jun",
136 "jul",
"aug",
"sep",
"oct",
"nov",
"dec"
138 static const std::array<const char*, MONTHS_PER_YEAR> full_names = {
139 "january",
"february",
"march",
"april",
"may",
"june",
140 "july",
"august",
"september",
"october",
"november",
"december"
143 for (std::size_t i = 0; i < short_names.size(); ++i) {
144 if (month_copy == short_names[i] || month_copy == full_names[i]) {
145 value =
static_cast<int>(i) + 1;
153# if __cplusplus >= 201703L
159 if (month.empty())
return false;
161 std::string month_copy;
163 if (month_copy.empty())
return false;
165 static const std::array<const char*, MONTHS_PER_YEAR> short_names = {
166 "jan",
"feb",
"mar",
"apr",
"may",
"jun",
167 "jul",
"aug",
"sep",
"oct",
"nov",
"dec"
169 static const std::array<const char*, MONTHS_PER_YEAR> full_names = {
170 "january",
"february",
"march",
"april",
"may",
"june",
171 "july",
"august",
"september",
"october",
"november",
"december"
174 for (std::size_t i = 0; i < short_names.size(); ++i) {
175 if (month_copy == short_names[i] || month_copy == full_names[i]) {
176 value =
static_cast<int>(i) + 1;
192 throw std::invalid_argument(
"Invalid month name");
197# if __cplusplus >= 201703L
205 throw std::invalid_argument(
"Invalid month name");
218 static const std::array<ZoneNameEntry, 25> entries = {{
250 if (data ==
nullptr || length == 0) {
256 for (std::size_t i = 0; i < entries.size(); ++i) {
257 const std::size_t name_length = std::strlen(entries[i].name);
258 if (length == name_length && std::memcmp(data, entries[i].name, name_length) == 0) {
259 zone = entries[i].zone;
274 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
'\f' || c ==
'\v';
279 return c >=
'0' && c <=
'9';
283 TIME_SHIELD_CONSTEXPR
inline void skip_spaces(
const char*& p,
const char* end)
noexcept {
291 TIME_SHIELD_CONSTEXPR
inline bool parse_2digits(
const char*& p,
const char* end,
int& out)
noexcept {
300 out = (a -
'0') * 10 + (b -
'0');
311 const char a = p[0], b = p[1], c = p[2], d = p[3];
315 const int v = (a -
'0') * 1000 + (b -
'0') * 100 + (c -
'0') * 10 + (d -
'0');
316 out =
static_cast<year_t>(v);
324 TIME_SHIELD_CONSTEXPR
inline bool parse_fraction_to_ms(
const char*& p,
const char* end,
int& ms_out)
noexcept {
336 ms = ms * 10 + (*p -
'0');
343 }
else if (digits == 2) {
377#if __cplusplus >= 201703L
402 template<
class T = Month>
412 template<
class T = Month>
416 value =
static_cast<T
>(idx);
420#if __cplusplus >= 201703L
426 template<
class T = Month>
436 template<
class T = Month>
440 value =
static_cast<T
>(idx);
471#if __cplusplus >= 201703L
495 template<
class T = Month>
501 template<
class T = Month>
511 template<
class T = Month>
517 template<
class T = Month>
523 template<
class T = Month>
528#if __cplusplus >= 201703L
530 template<
class T = Month>
536 template<
class T = Month>
542 template<
class T = Month>
548 template<
class T = Month>
554 template<
class T = Month>
567 template<
class T = Month>
569#if __cplusplus >= 201703L
581 template<
class T = Month>
583#if __cplusplus >= 201703L
591 template<
class T = Month>
597 template<
class T = Month>
603 template<
class T = Month>
634 tz.is_positive =
true;
638 if (length == 1 && (data[0] ==
'Z' || data[0] ==
'z')) {
641 tz.is_positive =
true;
649 const char sign = data[0];
650 if (sign !=
'+' && sign !=
'-') {
653 if (data[3] !=
':') {
661 tz.is_positive = (sign ==
'+');
662 tz.hour = (data[1] -
'0') * 10 + (data[2] -
'0');
663 tz.min = (data[4] -
'0') * 10 + (data[5] -
'0');
696 std::size_t begin = 0;
697 std::size_t end = length;
698 while (begin < end && std::isspace(
static_cast<unsigned char>(data[begin])) != 0) {
701 while (end > begin && std::isspace(
static_cast<unsigned char>(data[end - 1])) != 0) {
713#if __cplusplus >= 201703L
724 if (value ==
nullptr) {
741#if __cplusplus >= 201703L
782 const char* p = input;
783 const char* end = input + length;
789 tz.is_positive =
true;
791 const char*
const date_start = p;
792 const char* date_end = p;
797 bool parsed_iso_week_date =
false;
798 if (date_end > date_start) {
800 if (
parse_iso_week_date(date_start,
static_cast<std::size_t
>(date_end - date_start), iso_date)) {
802 dt.year = calendar_date.
year;
803 dt.mon = calendar_date.
mon;
804 dt.day = calendar_date.
day;
806 parsed_iso_week_date =
true;
810 if (!parsed_iso_week_date) {
818 const char sep1 = *p;
819 if (sep1 !=
'-' && sep1 !=
'/' && sep1 !=
'.') {
830 const char sep2 = *p;
831 if (sep2 !=
'-' && sep2 !=
'/' && sep2 !=
'.') {
860 if (*p ==
'T' || *p ==
't') {
874 if (p >= end || *p !=
':') {
885 bool has_seconds =
false;
888 if (p < end && *p ==
':') {
898 if (p < end && *p ==
'.') {
918 if (*p ==
'Z' || *p ==
'z') {
921 tz.is_positive =
true;
923 }
else if (*p ==
'+' || *p ==
'-') {
925 if (
static_cast<std::size_t
>(end - p) < 6) {
952 if (input ==
nullptr) {
958# if __cplusplus >= 201703L
991 if (!data || length == 0) {
1026 if (!data || length == 0) {
1061 if (!data || length == 0) {
1191 str_to_ts(str ? std::string(str) : std::string(), out);
1201 inline ts_t ts(
const char* data, std::size_t length) {
1215 str_to_ts_ms(str ? std::string(str) : std::string(), out);
1238 str_to_fts(str ? std::string(str) : std::string(), out);
1247 inline fts_t fts(
const char* data, std::size_t length) {
1303 template<
class T =
int>
1305 if (str.empty())
return false;
1307 const char* p = str.c_str();
1308 int parts[3] = {0, 0, 0};
1311 while (*p && idx < 3) {
1314 bool has_digit =
false;
1316 while (*p >=
'0' && *p <=
'9') {
1318 value = value * 10 + (*p -
'0');
1322 if (!has_digit)
return false;
1323 parts[idx++] = value;
1328 }
else if (*p ==
'\0') {
1335 if (idx == 0)
return false;
1336 if (!
is_valid_time(parts[0], parts[1], parts[2]))
return false;
1338 sec =
static_cast<T
>(
sec_of_day(parts[0], parts[1], parts[2]));
1352 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).
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.
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 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).
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).
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.
const std::array< ZoneNameEntry, 25 > & time_zone_name_entries() noexcept
Return supported strict named-zone entries.
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).
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.