3#ifndef _TIME_SHIELD_TIME_FORMAT_PARSER_HPP_INCLUDED
4#define _TIME_SHIELD_TIME_FORMAT_PARSER_HPP_INCLUDED
24#if __cplusplus >= 201703L
25# include <string_view>
118 return c >=
'0' && c <=
'9';
121 inline bool match_literal(
const char*& p,
const char* end,
char expected)
noexcept {
122 if (p >= end || *p != expected) {
129 inline bool match_literal(
const char*& p,
const char* end,
const char* literal)
noexcept {
134 if (p >= end || *p != *literal) {
147 out = (p[0] -
'0') * 10 + (p[1] -
'0');
157 int64_t& out)
noexcept {
158 const char* start = p;
162 value = value * 10 +
static_cast<int64_t
>(*p -
'0');
166 if (digits < min_digits) {
179 int64_t& out)
noexcept {
180 const char* start = p;
181 bool negative =
false;
182 if (p < end && (*p ==
'+' || *p ==
'-')) {
183 negative = (*p ==
'-');
191 out = negative ? -value : value;
207 inline bool parse_meridiem(
const char*& p,
const char* end,
bool uppercase,
bool& is_pm)
noexcept {
212 if (p[0] ==
'A' && p[1] ==
'M') {
214 }
else if (p[0] ==
'P' && p[1] ==
'M') {
220 if (p[0] ==
'a' && p[1] ==
'm') {
222 }
else if (p[0] ==
'p' && p[1] ==
'm') {
235 const char*
const* names,
239 for (std::size_t i = 0; i < count; ++i) {
240 const char* name = names[i];
241 const std::size_t len = std::strlen(name);
242 if (
static_cast<std::size_t
>(end - p) < len) {
246 for (std::size_t k = 0; k < len; ++k) {
247 if (p[k] != name[k]) {
253 p +=
static_cast<std::ptrdiff_t
>(len);
254 out =
static_cast<int>(i) + index_base;
262 static const char*
const uppercase_names[] = {
263 "JAN",
"FEB",
"MAR",
"APR",
"MAY",
"JUN",
264 "JUL",
"AUG",
"SEP",
"OCT",
"NOV",
"DEC"
266 static const char*
const short_names[] = {
267 "Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
268 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
270 static const char*
const full_names[] = {
271 "January",
"February",
"March",
"April",
"May",
"June",
272 "July",
"August",
"September",
"October",
"November",
"December"
287 static const char*
const uppercase_names[] = {
288 "SUN",
"MON",
"TUE",
"WED",
"THU",
"FRI",
"SAT"
290 static const char*
const short_names[] = {
291 "Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat"
293 static const char*
const full_names[] = {
294 "Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
311 if (end - p < 5 || (*p !=
'+' && *p !=
'-')) {
315 tz.is_positive = (*p ==
'+');
320 if (p < end && *p ==
':') {
330 if (has_field && field != value) {
339 if (has_field && field != value) {
348 return state.has_iso_week_year
349 || state.has_iso_week_two_digit_year
350 || state.has_iso_week;
354 return state.has_year
356 || state.has_two_digit_year
359 || state.has_day_of_year;
363 const int value =
static_cast<int>(
year % 100);
364 return value < 0 ? -value : value;
372 const int max_day = is_leap ? 366 : 365;
377 static const int days_per_month[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
380 while (month <= 12) {
381 int days = days_per_month[month - 1];
382 if (month == 2 && is_leap) {
385 if (remaining <=
days) {
396 static const int day_offsets[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
397 int result = day_offsets[month - 1] + day;
405 const char* start = p;
406 bool negative =
false;
407 if (p < end && (*p ==
'+' || *p ==
'-')) {
408 negative = (*p ==
'-');
418 int64_t year_value = 0;
419 if (p < end && *p ==
'M') {
421 int64_t millennia = 0;
427 if (p < end && *p ==
'K') {
433 year_value = head * 1000000LL + millennia * 1000LL + tail;
435 year_value = head * 1000000LL + millennia;
437 }
else if (p < end && *p ==
'K') {
444 year_value = head * 1000LL + tail;
449 out =
static_cast<year_t>(negative ? -year_value : year_value);
456 const char* format_data,
457 std::size_t format_size,
458 FormatParseState& state)
noexcept;
464 std::size_t repeat_count,
467 int64_t wide_value = 0;
470 return repeat_count == 1
474 return repeat_count == 1
478 return repeat_count == 1
482 return repeat_count == 1
486 return repeat_count == 1
492 return assign_int_field(state.has_century, state.century,
static_cast<int>(wide_value));
499 if (repeat_count == 1) {
502 if (repeat_count == 2) {
515 return repeat_count == 1
521 return assign_int_field(state.has_iso_week_two_digit_year, state.iso_week_two_digit_year,
static_cast<int>(wide_value));
533 if (repeat_count == 2) {
539 return repeat_count == 1
551 return assign_int_field(state.has_day_of_year, state.day_of_year,
static_cast<int>(wide_value));
563 if (repeat_count == 1) {
569 if (repeat_count == 2) {
577 if (repeat_count == 1) {
583 if (repeat_count == 2) {
589 if (repeat_count == 3) {
597 if (repeat_count != 1 || !
parse_meridiem(p, end,
true, state.is_pm)) {
600 state.has_meridiem =
true;
603 if (repeat_count != 1 || !
parse_meridiem(p, end,
false, state.is_pm)) {
606 state.has_meridiem =
true;
609 return repeat_count == 1
612 return repeat_count == 1
615 if (repeat_count == 1) {
619 state.has_unix_seconds =
true;
620 state.unix_seconds =
static_cast<ts_t>(wide_value);
623 if (repeat_count == 3) {
627 return assign_int_field(state.has_millisecond, state.millisecond,
static_cast<int>(wide_value));
631 if (repeat_count <= 2) {
637 if (repeat_count == 3) {
641 return assign_int_field(state.has_millisecond, state.millisecond,
static_cast<int>(wide_value));
647 return repeat_count == 1
653 return assign_int_field(state.has_iso_weekday, state.iso_weekday,
static_cast<int>(wide_value));
655 if (repeat_count == 1) {
659 return assign_int_field(state.has_weekday, state.weekday,
static_cast<int>(wide_value));
661 if (repeat_count == 3) {
667 if (repeat_count == 3) {
681 return assign_int_field(state.has_two_digit_year, state.two_digit_year,
static_cast<int>(wide_value));
683 if (repeat_count == 1) {
689 if (repeat_count == 2) {
693 return assign_int_field(state.has_two_digit_year, state.two_digit_year,
static_cast<int>(wide_value));
695 if (repeat_count == 4) {
701 if (repeat_count == 6) {
730 const char* format_data,
731 std::size_t format_size,
733 bool is_command =
false;
734 std::size_t repeat_count = 0;
737 for (std::size_t i = 0; i < format_size; ++i) {
738 const char current_char = format_data[i];
740 if (current_char ==
'%') {
742 if (repeat_count == 2) {
756 last_char = current_char;
760 if (last_char == current_char) {
786 if (state.has_iso_week_year && iso_week.
year != state.iso_week_year) {
792 if (state.has_iso_week && iso_week.
week != state.iso_week) {
795 if (state.has_iso_weekday && iso_week.
weekday != state.iso_weekday) {
804 if (state.has_hour24) {
806 if (state.has_meridiem && state.has_hour12) {
807 int expected = state.hour12 % 12;
811 if (expected != hour) {
815 }
else if (state.has_hour12) {
816 if (!state.has_meridiem || state.hour12 < 1 || state.hour12 > 12) {
819 hour = state.hour12 % 12;
825 minute = state.has_minute ? state.minute : 0;
826 second = state.has_second ? state.second : 0;
827 millisecond = state.has_millisecond ? state.millisecond : 0;
840 if (state.has_iso_week_year) {
841 iso_week_year = state.iso_week_year;
842 if (state.has_iso_week_two_digit_year
846 }
else if (state.has_iso_week_two_digit_year) {
847 iso_week_year =
static_cast<year_t>(state.iso_week_two_digit_year);
852 if (!state.has_iso_week) {
859 state.has_iso_weekday ? state.iso_weekday : 1);
888 if (state.has_year) {
890 if (state.has_century &&
year / 100 != state.century) {
893 const int yy =
static_cast<int>(
year >= 0 ? (
year % 100) : -(
year % 100));
894 if (state.has_two_digit_year && yy != state.two_digit_year) {
897 }
else if (state.has_century || state.has_two_digit_year) {
898 year =
static_cast<year_t>((state.has_century ? state.century : 0) * 100
899 + (state.has_two_digit_year ? state.two_digit_year : 0));
904 int month = state.has_month ? state.month : 0;
905 int day = state.has_day ? state.day : 0;
906 if (state.has_day_of_year) {
907 int resolved_month = 0;
908 int resolved_day = 0;
912 if (state.has_month && state.month != resolved_month) {
915 if (state.has_day && state.day != resolved_day) {
918 month = resolved_month;
921 if (month == 0 || day == 0) {
955 if (state.has_unix_seconds) {
958 const ts_t local_ts = state.has_tz ?
static_cast<ts_t>(state.unix_seconds + offset)
959 : state.unix_seconds;
961 if (state.has_millisecond) {
962 out_dt.ms = state.millisecond;
965 if (state.has_year && out_dt.year != state.year)
return false;
966 if (state.has_month && out_dt.mon != state.month)
return false;
967 if (state.has_day && out_dt.day != state.day)
return false;
968 if (state.has_hour24 && out_dt.hour != state.hour24)
return false;
969 if (state.has_minute && out_dt.min != state.minute)
return false;
970 if (state.has_second && out_dt.sec != state.second)
return false;
971 if (state.has_millisecond && out_dt.ms != state.millisecond)
return false;
972 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;
983 std::size_t format_length,
986 if (!data || !format) {
990 const char* p = data;
991 const char* end = data + length;
1013 std::size_t format_length,
1025 std::size_t format_length,
1026 ts_t& out_ts)
noexcept {
1048 std::size_t format_length,
1068 const std::string& data,
1069 const std::string& format,
1072 return try_parse_format(data.data(), data.size(), format.data(), format.size(), out_dt, out_tz);
1082 if (!data || !format) {
1085 return try_parse_format(data, std::strlen(data), format, std::strlen(format), out_dt, out_tz);
1091 const std::string& data,
1092 const std::string& format,
1093 ts_t& out_ts)
noexcept {
1094 return try_parse_format_ts(data.data(), data.size(), format.data(), format.size(), out_ts);
1102 ts_t& out_ts)
noexcept {
1103 if (!data || !format) {
1113 const std::string& data,
1114 const std::string& format,
1125 if (!data || !format) {
1132#if __cplusplus >= 201703L
1136 std::string_view data,
1137 std::string_view format,
1140 return try_parse_format(data.data(), data.size(), format.data(), format.size(), out_dt, out_tz);
1146 std::string_view data,
1147 std::string_view format,
1148 ts_t& out_ts)
noexcept {
1149 return try_parse_format_ts(data.data(), data.size(), format.data(), format.size(), out_ts);
1155 std::string_view data,
1156 std::string_view format,
Configuration macros for the library.
Header file with time-related constants.
Conversions involving DateTimeStruct and day boundary helpers.
Header for date and time structure and related functions.
Header file with enumerations for weekdays, months, and other time-related categories.
@ 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.
Conversions and utilities for ISO week dates (ISO 8601).
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.
Header for time zone structure and related functions.
Header file with time-related validation functions.