Time Shield Library
C++ library for working with time
Loading...
Searching...
No Matches
time_parser.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2#pragma once
3#ifndef _TIME_SHIELD_TIME_PARSER_HPP_INCLUDED
4#define _TIME_SHIELD_TIME_PARSER_HPP_INCLUDED
5
11
12#include "enums.hpp"
13#include "constants.hpp"
14#include "date_time_struct.hpp"
15#include "time_zone_struct.hpp"
16#include "validation.hpp"
17#include "time_conversions.hpp"
18#include <regex>
19#include <algorithm>
20#include <locale>
21#include <array>
22#include <stdexcept>
23#include <sstream>
24#include <cctype>
25
26namespace time_shield {
27
59
65 template<class T = Month>
66 T get_month_number(const std::string& month) {
67 if (month.empty()) throw std::invalid_argument("Invalid month name");
68
69 std::string month_copy = month;
70 std::transform(month_copy.begin(), month_copy.end(), month_copy.begin(), [](char &ch) {
71 return std::use_facet<std::ctype<char>>(std::locale()).tolower(ch);
72 });
73 month_copy[0] = static_cast<char>(std::toupper(month_copy[0]));
74
75 static const std::array<std::string, 12> short_names = {
76 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
77 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
78 };
79 static const std::array<std::string, 12> full_names = {
80 "January", "February", "March", "April", "May", "June",
81 "July", "August", "September", "October", "November", "December"
82 };
83
84 for(int i = 0; i < MONTHS_PER_YEAR; ++i) {
85 if (month == short_names[i] || month == full_names[i]) {
86 return static_cast<T>(i + 1);
87 }
88 }
89 throw std::invalid_argument("Invalid month name");
90 }
91
94 template<class T = Month>
95 T month_of_year(const std::string& month) {
96 return get_month_number(month);
97 }
98
99//------------------------------------------------------------------------------
100
106 template<class T = Month>
107 bool try_get_month_number(const std::string& month, T& value) {
108 if (month.empty()) return false;
109
110 std::string month_copy = month;
111 std::transform(month_copy.begin(), month_copy.end(), month_copy.begin(), [](char &ch) {
112 return std::use_facet<std::ctype<char>>(std::locale()).tolower(ch);
113 });
114 month_copy[0] = static_cast<char>(std::toupper(month_copy[0]));
115
116 static const std::array<std::string, MONTHS_PER_YEAR> short_names = {
117 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
118 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
119 };
120 static const std::array<std::string, MONTHS_PER_YEAR> full_names = {
121 "January", "February", "March", "April", "May", "June",
122 "July", "August", "September", "October", "November", "December"
123 };
124
125 for (int i = 0; i < MONTHS_PER_YEAR; ++i) {
126 if (month_copy == short_names[i] || month_copy == full_names[i]) {
127 value = static_cast<T>(i + 1);
128 return true;
129 }
130 }
131 return false;
132 }
133
136 template<class T = Month>
137 bool get_month_number(const std::string& month, T& value) {
138 return try_get_month_number(month, value);
139 }
140
143 template<class T = Month>
144 bool month_of_year(const std::string& month, T& value) {
145 return try_get_month_number(month, value);
146 }
147
148//------------------------------------------------------------------------------
149
156 inline bool parse_time_zone(const std::string& tz_str, TimeZoneStruct &tz) {
157 if (tz_str.empty()) {
158 tz.hour = 0;
159 tz.min = 0;
160 tz.is_positive = true;
161 return true;
162 }
163 if (tz_str == "Z") {
164 tz.hour = 0;
165 tz.min = 0;
166 tz.is_positive = true;
167 return true;
168 }
169 tz.is_positive = (tz_str[0] == '+');
170 tz.hour = std::stoi(tz_str.substr(1, 2));
171 tz.min = std::stoi(tz_str.substr(4, 2));
172 return is_valid_time_zone(tz);
173 }
174
177 inline bool parse_tz(const std::string& tz_str, TimeZoneStruct &tz) {
178 return parse_time_zone(tz_str, tz);
179 }
180
181//------------------------------------------------------------------------------
182
190 inline bool parse_iso8601(
191 const std::string& input,
192 DateTimeStruct &dt,
193 TimeZoneStruct &tz) {
194 // Регулярное выражение для даты в формате ISO8601
195 static const std::regex date_regex(R"((\d{4})[-\/\.](\d{2})[-\/\.](\d{2}))");
196 std::smatch date_match;
197
198 // Регулярное выражение для времени в формате ISO8601 с часовым поясом и без
199 static const std::regex time_regex(R"((\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(Z|[+-]\d{2}:\d{2})?)");
200 std::smatch time_match;
201
203 tz = create_time_zone_struct(0, 0);
204
205 // Парсинг даты
206 if (std::regex_search(input, date_match, date_regex)) {
207 dt.year = std::stoll(date_match[1].str());
208 dt.mon = std::stoi(date_match[2].str());
209 dt.day = std::stoi(date_match[3].str());
210 } else {
211 return false;
212 }
213
214 // Парсинг времени и часового пояса
215 if (std::regex_search(input, time_match, time_regex)) {
216 dt.hour = std::stoi(time_match[1].str());
217 dt.min = std::stoi(time_match[2].str());
218 dt.sec = std::stoi(time_match[3].str());
219 if (time_match[4].matched) {
220 dt.ms = std::stoi(time_match[4].str());
221 }
222 if (time_match[5].matched) {
223 if (!parse_time_zone(time_match[5].str(), tz)) return false;
224 }
225 return is_valid_date_time(dt);
226 }
227 return true;
228 }
229
235 inline bool str_to_ts(const std::string &str, ts_t& ts) {
238 if (!parse_iso8601(str, dt, tz)) return false;
239 try {
240 ts = to_timestamp(dt) + to_offset(tz);
241 return true;
242 } catch(...) {}
243 return false;
244 }
245
251 inline bool str_to_ts_ms(const std::string &str, ts_ms_t& ts) {
254 if (!parse_iso8601(str, dt, tz)) return false;
255 try {
257 return true;
258 } catch(...) {}
259 return false;
260 }
261
265 inline bool is_workday(const std::string& str) {
266 ts_t ts = 0;
267 if (!str_to_ts(str, ts)) {
268 return false;
269 }
270 return is_workday(ts);
271 }
272
276 inline bool is_workday_ms(const std::string& str) {
277 ts_ms_t ts = 0;
278 if (!str_to_ts_ms(str, ts)) {
279 return false;
280 }
281 return is_workday_ms(ts);
282 }
283
286 inline bool workday(const std::string& str) {
287 return is_workday(str);
288 }
289
292 inline bool workday_ms(const std::string& str) {
293 return is_workday_ms(str);
294 }
295
299 inline bool is_first_workday_of_month(const std::string& str) {
300 ts_t ts = 0;
301 if (!str_to_ts(str, ts)) {
302 return false;
303 }
305 }
306
310 inline bool is_first_workday_of_month_ms(const std::string& str) {
311 ts_ms_t ts = 0;
312 if (!str_to_ts_ms(str, ts)) {
313 return false;
314 }
316 }
317
322 inline bool is_within_first_workdays_of_month(const std::string& str, int count) {
323 ts_t ts = 0;
324 if (!str_to_ts(str, ts)) {
325 return false;
326 }
328 }
329
334 inline bool is_within_first_workdays_of_month_ms(const std::string& str, int count) {
335 ts_ms_t ts = 0;
336 if (!str_to_ts_ms(str, ts)) {
337 return false;
338 }
340 }
341
345 inline bool is_last_workday_of_month(const std::string& str) {
346 ts_t ts = 0;
347 if (!str_to_ts(str, ts)) {
348 return false;
349 }
351 }
352
356 inline bool is_last_workday_of_month_ms(const std::string& str) {
357 ts_ms_t ts = 0;
358 if (!str_to_ts_ms(str, ts)) {
359 return false;
360 }
362 }
363
368 inline bool is_within_last_workdays_of_month(const std::string& str, int count) {
369 ts_t ts = 0;
370 if (!str_to_ts(str, ts)) {
371 return false;
372 }
374 }
375
380 inline bool is_within_last_workdays_of_month_ms(const std::string& str, int count) {
381 ts_ms_t ts = 0;
382 if (!str_to_ts_ms(str, ts)) {
383 return false;
384 }
386 }
387
393 inline bool str_to_fts(const std::string &str, fts_t& ts) {
396 if (!parse_iso8601(str, dt, tz)) return false;
397 try {
398 ts = to_ftimestamp(dt) + static_cast<fts_t>(to_offset(tz));
399 return true;
400 } catch(...) {}
401 return false;
402 }
403
409 inline ts_t ts(const std::string& str) {
410 ts_t ts = 0;
411 str_to_ts(str, ts);
412 return ts;
413 }
414
420 inline ts_ms_t ts_ms(const std::string& str) {
421 ts_ms_t ts = 0;
422 str_to_ts_ms(str, ts);
423 return ts;
424 }
425
431 inline fts_t fts(const std::string& str) {
432 fts_t ts = 0;
433 str_to_fts(str, ts);
434 return ts;
435 }
436
437 //--------------------------------------------------------------------------
438
450 template<class T = int>
451 inline bool sec_of_day(const std::string& str, T& sec) {
452 if (str.empty()) return false;
453
454 const char* p = str.c_str();
455 int parts[3] = {0, 0, 0}; // hour, minute, second
456 int idx = 0;
457
458 while (*p && idx < 3) {
459 // Parse integer
460 int value = 0;
461 bool has_digit = false;
462
463 while (*p >= '0' && *p <= '9') {
464 has_digit = true;
465 value = value * 10 + (*p - '0');
466 ++p;
467 }
468
469 if (!has_digit) return false;
470 parts[idx++] = value;
471
472 // Expect colon or end
473 if (*p == ':') {
474 ++p;
475 } else if (*p == '\0') {
476 break;
477 } else {
478 return false; // unexpected character
479 }
480 }
481
482 if (idx == 0) return false;
483 if (!is_valid_time(parts[0], parts[1], parts[2])) return false;
484
485 sec = static_cast<T>(sec_of_day(parts[0], parts[1], parts[2]));
486 return true;
487 }
488
499 template<class T = int>
500 inline T sec_of_day(const std::string& str) {
501 T value{};
502 if (sec_of_day(str, value))
503 return value;
504 return static_cast<T>(SEC_PER_DAY);
505 }
506
507
513 inline ts_t ts(const char *str) {
514 ts_t ts = 0;
515 str_to_ts(str, ts);
516 return ts;
517 }
518
524 inline ts_ms_t ts_ms(const char *str) {
525 ts_ms_t ts = 0;
526 str_to_ts_ms(str, ts);
527 return ts;
528 }
529
535 inline fts_t fts(const char *str) {
536 fts_t ts = 0;
537 str_to_fts(str, ts);
538 return ts;
539 }
540
542
543};
544
545#endif // _TIME_SHIELD_TIME_PARSER_HPP_INCLUDED
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.
const int64_t MONTHS_PER_YEAR
Months per year.
constexpr int64_t SEC_PER_DAY
Seconds per day.
TIME_SHIELD_CONSTEXPR bool is_first_workday_of_month_ms(ts_ms_t ts_ms) noexcept
Check whether a timestamp in milliseconds falls on the first workday of its month.
TIME_SHIELD_CONSTEXPR T month_of_year(ts_t ts) noexcept
Get the month of the year.
TIME_SHIELD_CONSTEXPR bool is_within_last_workdays_of_month_ms(ts_ms_t ts_ms, int count) noexcept
Check whether a timestamp in milliseconds falls within the last N workdays of its month.
TIME_SHIELD_CONSTEXPR bool is_within_first_workdays_of_month(year_t year, int month, int day, int count) noexcept
Check whether a date is within the first N workdays of its month.
TIME_SHIELD_CONSTEXPR bool workday(ts_t ts) noexcept
Alias for is_workday(ts_t).
constexpr T sec_of_day(ts_t ts=time_shield::ts()) noexcept
Get the second of the day.
TIME_SHIELD_CONSTEXPR bool is_within_first_workdays_of_month_ms(ts_ms_t ts_ms, int count) noexcept
Check whether a timestamp in milliseconds falls within the first N workdays of its month.
TIME_SHIELD_CONSTEXPR ts_t ts(year_t year, int month, int day)
Alias for to_timestamp.
TIME_SHIELD_CONSTEXPR bool is_last_workday_of_month(year_t year, int month, int day) noexcept
Check whether a date is the last workday of its month.
TIME_SHIELD_CONSTEXPR bool is_first_workday_of_month(year_t year, int month, int day) noexcept
Check whether a date is the first workday of its month.
TIME_SHIELD_CONSTEXPR bool is_last_workday_of_month_ms(ts_ms_t ts_ms) noexcept
Check whether a timestamp in milliseconds falls on the last workday of its month.
TIME_SHIELD_CONSTEXPR bool is_within_last_workdays_of_month(year_t year, int month, int day, int count) noexcept
Check whether a date is within the last N workdays of its month.
constexpr fts_t to_ftimestamp(const T &date_time)
Alias for dt_to_ftimestamp.
TIME_SHIELD_CONSTEXPR ts_t to_timestamp(const T &date_time)
Alias for dt_to_timestamp function.
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.
TIME_SHIELD_CONSTEXPR ts_t to_timestamp_ms(const T &date_time)
Alias for dt_to_timestamp_ms function.
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 parse_iso8601(const std::string &input, DateTimeStruct &dt, TimeZoneStruct &tz)
Parse a date and time string in ISO8601 format.
bool try_get_month_number(const std::string &month, T &value)
Get the month number by name, with output parameter.
bool is_workday(const std::string &str)
Parse an ISO8601 string and check if it falls on a workday using second precision.
T get_month_number(const std::string &month)
Get the month number by name.
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 parse_time_zone(const std::string &tz_str, TimeZoneStruct &tz)
Parse a time zone string into a TimeZoneStruct.
bool parse_tz(const std::string &tz_str, TimeZoneStruct &tz)
Alias for parse_time_zone function.
bool is_workday_ms(const std::string &str)
Parse an ISO8601 string and check if it falls on a workday using millisecond precision.
tz_t to_offset(const TimeZoneStruct &tz)
Alias for time_zone_struct_to_offset function.
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.
Definition types.hpp:46
int64_t ts_ms_t
Unix timestamp in milliseconds since epoch.
Definition types.hpp:47
double fts_t
Floating-point timestamp (fractional seconds since epoch).
Definition types.hpp:49
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_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.
Main namespace for the Time Shield library.
Structure to represent date and time.
int ms
Millisecond component of time (0-999)
int hour
Hour component of time (0-23)
int64_t year
Year component of the date.
int day
Day component of the date (1-31).
int min
Minute component of time (0-59)
int mon
Month component of the date (1-12).
int sec
Second component of time (0-59)
Structure to represent time zone information.
int hour
Hour component of time (0-23)
int min
Minute component of time (0-59)
bool is_positive
True if the time zone offset is positive, false if negative.
Header file for time conversion functions.
Header for time zone structure and related functions.
Header file with time-related validation functions.