3#ifndef TIME_SHIELD_HEADER_TEXT_TIME_FORMAT_PARSER_HPP_INCLUDED
4#define TIME_SHIELD_HEADER_TEXT_TIME_FORMAT_PARSER_HPP_INCLUDED
18#if __cplusplus >= 201703L
19# include <string_view>
25 namespace format_parse {
112 return c >=
'0' && c <=
'9';
115 inline bool match_literal(
const char*& p,
const char* end,
char expected)
noexcept {
116 if (p >= end || *p != expected) {
123 inline bool match_literal(
const char*& p,
const char* end,
const char* literal)
noexcept {
128 if (p >= end || *p != *literal) {
141 out = (p[0] -
'0') * 10 + (p[1] -
'0');
151 int64_t& out)
noexcept {
152 const char* start = p;
156 value = value * 10 +
static_cast<int64_t
>(*p -
'0');
160 if (digits < min_digits) {
173 int64_t& out)
noexcept {
174 const char* start = p;
175 bool negative =
false;
176 if (p < end && (*p ==
'+' || *p ==
'-')) {
177 negative = (*p ==
'-');
185 out = negative ? -value : value;
201 inline bool parse_meridiem(
const char*& p,
const char* end,
bool uppercase,
bool& is_pm)
noexcept {
206 if (p[0] ==
'A' && p[1] ==
'M') {
208 }
else if (p[0] ==
'P' && p[1] ==
'M') {
214 if (p[0] ==
'a' && p[1] ==
'm') {
216 }
else if (p[0] ==
'p' && p[1] ==
'm') {
229 const char*
const* names,
233 for (std::size_t i = 0; i < count; ++i) {
234 const char* name = names[i];
235 const std::size_t len = std::strlen(name);
236 if (
static_cast<std::size_t
>(end - p) < len) {
240 for (std::size_t k = 0; k < len; ++k) {
241 if (p[k] != name[k]) {
247 p +=
static_cast<std::ptrdiff_t
>(len);
248 out =
static_cast<int>(i) + index_base;
256 static const char*
const uppercase_names[] = {
257 "JAN",
"FEB",
"MAR",
"APR",
"MAY",
"JUN",
258 "JUL",
"AUG",
"SEP",
"OCT",
"NOV",
"DEC"
260 static const char*
const short_names[] = {
261 "Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
262 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
264 static const char*
const full_names[] = {
265 "January",
"February",
"March",
"April",
"May",
"June",
266 "July",
"August",
"September",
"October",
"November",
"December"
281 static const char*
const uppercase_names[] = {
282 "SUN",
"MON",
"TUE",
"WED",
"THU",
"FRI",
"SAT"
284 static const char*
const short_names[] = {
285 "Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat"
287 static const char*
const full_names[] = {
288 "Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
305 if (end - p < 5 || (*p !=
'+' && *p !=
'-')) {
309 tz.is_positive = (*p ==
'+');
314 if (p < end && *p ==
':') {
324 if (has_field && field != value) {
333 if (has_field && field != value) {
342 return state.has_iso_week_year
343 || state.has_iso_week_two_digit_year
344 || state.has_iso_week;
348 return state.has_year
350 || state.has_two_digit_year
353 || state.has_day_of_year;
357 const int value =
static_cast<int>(
year % 100);
358 return value < 0 ? -value : value;
366 const int max_day = is_leap ? 366 : 365;
371 static const int days_per_month[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
374 while (month <= 12) {
375 int days = days_per_month[month - 1];
376 if (month == 2 && is_leap) {
379 if (remaining <=
days) {
390 static const int day_offsets[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
391 int result = day_offsets[month - 1] + day;
399 const char* start = p;
400 bool negative =
false;
401 if (p < end && (*p ==
'+' || *p ==
'-')) {
402 negative = (*p ==
'-');
412 int64_t year_value = 0;
413 if (p < end && *p ==
'M') {
415 int64_t millennia = 0;
421 if (p < end && *p ==
'K') {
427 year_value = head * 1000000LL + millennia * 1000LL + tail;
429 year_value = head * 1000000LL + millennia;
431 }
else if (p < end && *p ==
'K') {
438 year_value = head * 1000LL + tail;
443 out =
static_cast<year_t>(negative ? -year_value : year_value);
450 const char* format_data,
451 std::size_t format_size,
452 FormatParseState& state)
noexcept;
458 std::size_t repeat_count,
461 int64_t wide_value = 0;
464 return repeat_count == 1
468 return repeat_count == 1
472 return repeat_count == 1
476 return repeat_count == 1
480 return repeat_count == 1
486 return assign_int_field(state.has_century, state.century,
static_cast<int>(wide_value));
493 if (repeat_count == 1) {
496 if (repeat_count == 2) {
509 return repeat_count == 1
515 return assign_int_field(state.has_iso_week_two_digit_year, state.iso_week_two_digit_year,
static_cast<int>(wide_value));
527 if (repeat_count == 2) {
533 return repeat_count == 1
545 return assign_int_field(state.has_day_of_year, state.day_of_year,
static_cast<int>(wide_value));
557 if (repeat_count == 1) {
563 if (repeat_count == 2) {
571 if (repeat_count == 1) {
577 if (repeat_count == 2) {
583 if (repeat_count == 3) {
591 if (repeat_count != 1 || !
parse_meridiem(p, end,
true, state.is_pm)) {
594 state.has_meridiem =
true;
597 if (repeat_count != 1 || !
parse_meridiem(p, end,
false, state.is_pm)) {
600 state.has_meridiem =
true;
603 return repeat_count == 1
606 return repeat_count == 1
609 if (repeat_count == 1) {
613 state.has_unix_seconds =
true;
614 state.unix_seconds =
static_cast<ts_t>(wide_value);
617 if (repeat_count == 3) {
621 return assign_int_field(state.has_millisecond, state.millisecond,
static_cast<int>(wide_value));
625 if (repeat_count <= 2) {
631 if (repeat_count == 3) {
635 return assign_int_field(state.has_millisecond, state.millisecond,
static_cast<int>(wide_value));
641 return repeat_count == 1
647 return assign_int_field(state.has_iso_weekday, state.iso_weekday,
static_cast<int>(wide_value));
649 if (repeat_count == 1) {
653 return assign_int_field(state.has_weekday, state.weekday,
static_cast<int>(wide_value));
655 if (repeat_count == 3) {
661 if (repeat_count == 3) {
675 return assign_int_field(state.has_two_digit_year, state.two_digit_year,
static_cast<int>(wide_value));
677 if (repeat_count == 1) {
683 if (repeat_count == 2) {
687 return assign_int_field(state.has_two_digit_year, state.two_digit_year,
static_cast<int>(wide_value));
689 if (repeat_count == 4) {
695 if (repeat_count == 6) {
724 const char* format_data,
725 std::size_t format_size,
727 bool is_command =
false;
728 std::size_t repeat_count = 0;
731 for (std::size_t i = 0; i < format_size; ++i) {
732 const char current_char = format_data[i];
734 if (current_char ==
'%') {
736 if (repeat_count == 2) {
750 last_char = current_char;
754 if (last_char == current_char) {
780 if (state.has_iso_week_year && iso_week.
year != state.iso_week_year) {
786 if (state.has_iso_week && iso_week.
week != state.iso_week) {
789 if (state.has_iso_weekday && iso_week.
weekday != state.iso_weekday) {
798 if (state.has_hour24) {
800 if (state.has_meridiem && state.has_hour12) {
801 int expected = state.hour12 % 12;
805 if (expected != hour) {
809 }
else if (state.has_hour12) {
810 if (!state.has_meridiem || state.hour12 < 1 || state.hour12 > 12) {
813 hour = state.hour12 % 12;
819 minute = state.has_minute ? state.minute : 0;
820 second = state.has_second ? state.second : 0;
821 millisecond = state.has_millisecond ? state.millisecond : 0;
834 if (state.has_iso_week_year) {
835 iso_week_year = state.iso_week_year;
836 if (state.has_iso_week_two_digit_year
840 }
else if (state.has_iso_week_two_digit_year) {
841 iso_week_year =
static_cast<year_t>(state.iso_week_two_digit_year);
846 if (!state.has_iso_week) {
853 state.has_iso_weekday ? state.iso_weekday : 1);
882 if (state.has_year) {
884 if (state.has_century &&
year / 100 != state.century) {
887 const int yy =
static_cast<int>(
year >= 0 ? (
year % 100) : -(
year % 100));
888 if (state.has_two_digit_year && yy != state.two_digit_year) {
891 }
else if (state.has_century || state.has_two_digit_year) {
892 year =
static_cast<year_t>((state.has_century ? state.century : 0) * 100
893 + (state.has_two_digit_year ? state.two_digit_year : 0));
898 int month = state.has_month ? state.month : 0;
899 int day = state.has_day ? state.day : 0;
900 if (state.has_day_of_year) {
901 int resolved_month = 0;
902 int resolved_day = 0;
906 if (state.has_month && state.month != resolved_month) {
909 if (state.has_day && state.day != resolved_day) {
912 month = resolved_month;
915 if (month == 0 || day == 0) {
949 if (state.has_unix_seconds) {
952 const ts_t local_ts = state.has_tz ?
static_cast<ts_t>(state.unix_seconds + offset)
953 : state.unix_seconds;
955 if (state.has_millisecond) {
956 out_dt.ms = state.millisecond;
959 if (state.has_year && out_dt.year != state.year)
return false;
960 if (state.has_month && out_dt.mon != state.month)
return false;
961 if (state.has_day && out_dt.day != state.day)
return false;
962 if (state.has_hour24 && out_dt.hour != state.hour24)
return false;
963 if (state.has_minute && out_dt.min != state.minute)
return false;
964 if (state.has_second && out_dt.sec != state.second)
return false;
965 if (state.has_millisecond && out_dt.ms != state.millisecond)
return false;
966 if (state.has_day_of_year &&
compute_day_of_year(out_dt.year, out_dt.mon, out_dt.day) != state.day_of_year)
return false;
977 std::size_t format_length,
980 if (!data || !format) {
984 const char* p = data;
985 const char* end = data + length;
1007 std::size_t format_length,
1019 std::size_t format_length,
1020 ts_t& out_ts)
noexcept {
1042 std::size_t format_length,
1062 const std::string& data,
1063 const std::string& format,
1066 return try_parse_format(data.data(), data.size(), format.data(), format.size(), out_dt, out_tz);
1076 if (!data || !format) {
1079 return try_parse_format(data, std::strlen(data), format, std::strlen(format), out_dt, out_tz);
1085 const std::string& data,
1086 const std::string& format,
1087 ts_t& out_ts)
noexcept {
1088 return try_parse_format_ts(data.data(), data.size(), format.data(), format.size(), out_ts);
1096 ts_t& out_ts)
noexcept {
1097 if (!data || !format) {
1107 const std::string& data,
1108 const std::string& format,
1119 if (!data || !format) {
1126#if __cplusplus >= 201703L
1130 std::string_view data,
1131 std::string_view format,
1134 return try_parse_format(data.data(), data.size(), format.data(), format.size(), out_dt, out_tz);
1140 std::string_view data,
1141 std::string_view format,
1142 ts_t& out_ts)
noexcept {
1143 return try_parse_format_ts(data.data(), data.size(), format.data(), format.size(), out_ts);
1149 std::string_view data,
1150 std::string_view format,
@ UPPERCASE_NAME
Uppercase short name.
bool is_valid_iso_week_date(year_t iso_year, int week, int weekday)
Validate ISO week date components.
TIME_SHIELD_CONSTEXPR T1 day_of_week_date(T2 year, T3 month, T4 day)
Get the day of the week.
IsoWeekDateStruct to_iso_week_date(Y year, M month, D day)
Convert calendar date to ISO week date.
DateStruct iso_week_date_to_date(const IsoWeekDateStruct &iso_date)
Convert ISO week date to calendar date.
TIME_SHIELD_CONSTEXPR T days(ts_t start, ts_t stop) noexcept
Alias for days_between function.
TIME_SHIELD_CONSTEXPR T1 weekday(year_t year, int month, int day)
Alias for day_of_week_date.
TIME_SHIELD_CONSTEXPR T year(ts_t ts=time_shield::ts())
Alias for year_of function.
TIME_SHIELD_CONSTEXPR T1 sec_to_ms(T2 ts) noexcept
Converts a timestamp from seconds to milliseconds.
bool try_parse_format(const char *data, std::size_t length, const char *format, std::size_t format_length, DateTimeStruct &out_dt, TimeZoneStruct &out_tz) noexcept
Parse input using formatter-compatible custom pattern.
bool try_parse_format_ts_ms(const char *data, std::size_t length, const char *format, std::size_t format_length, ts_ms_t &out_ts) noexcept
Parse input using formatter-compatible custom pattern and convert to UTC milliseconds.
bool try_parse_format_ts(const char *data, std::size_t length, const char *format, std::size_t format_length, ts_t &out_ts) noexcept
Parse input using formatter-compatible custom pattern and convert to UTC seconds.
TIME_SHIELD_CONSTEXPR tz_t time_zone_struct_to_offset(const TimeZoneStruct &tz) noexcept
Convert a TimeZoneStruct to a numeric UTC offset (seconds).
T day_of_year(ts_t ts=time_shield::ts())
Get the day of the year.
TIME_SHIELD_CONSTEXPR bool is_valid_time_zone_offset(const T &time_zone) noexcept
Check if the time zone is valid.
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 IsoWeekDateStruct create_iso_week_date_struct(int64_t year, int32_t week=1, int32_t weekday=1)
Creates an IsoWeekDateStruct instance.
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.
T1 to_date_time(T2 ts)
Converts a timestamp to a date-time structure.
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.
int32_t tz_t
Time zone offset in minutes from UTC (e.g., +180 = UTC+3).
int64_t ts_ms_t
Unix timestamp in milliseconds since epoch.
int64_t year_t
Year as an integer (e.g., 2024).
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_leap_year_date(T year) noexcept
Checks if the given year is a leap year.
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.
int64_t year
ISO week-numbering year component.
int32_t week
ISO week number component (1-52/53).
int32_t weekday
ISO weekday component (1=Monday .. 7=Sunday).
Structure to represent time zone information.