3636#include < stdexcept>
3737#include < type_traits>
3838
39+ // NOLINTBEGIN(modernize-concat-nested-namespaces)
3940namespace mgutility {
4041namespace chrono {
4142namespace detail {
43+ // NOLINTEND(modernize-concat-nested-namespaces)
4244
4345/* *
4446 * @brief Extended tm structure with milliseconds.
@@ -61,8 +63,6 @@ template <typename T = int32_t>
6163MGUTILITY_CNSTXPR auto parse_integer (T &result, mgutility::string_view str,
6264 uint32_t len, uint32_t &next,
6365 uint32_t begin_offset = 0 ) -> std::errc {
64- result = 0 ;
65-
6666 auto error = mgutility::from_chars (str.data () + next + begin_offset,
6767 str.data () + len + next, result);
6868
@@ -79,8 +79,8 @@ MGUTILITY_CNSTXPR auto parse_integer(T &result, mgutility::string_view str,
7979 * @param max The maximum acceptable value.
8080 * @throws std::out_of_range if the value is out of range.
8181 */
82- MGUTILITY_CNSTXPR auto check_range ( int32_t value, int32_t min, int32_t max)
83- -> std::errc {
82+ template < typename T = int32_t >
83+ MGUTILITY_CNSTXPR auto check_range ( const T& value, const int32_t & min, const int32_t & max) -> std::errc {
8484 if (value < min || value > max) {
8585 return std::errc::result_out_of_range;
8686 }
@@ -104,7 +104,7 @@ auto MGUTILITY_CNSTXPR is_leap_year(uint32_t year) -> bool {
104104 * @return std::time_t The corresponding time_t value.
105105 * @throws std::out_of_range if any tm value is out of valid range.
106106 */
107- MGUTILITY_CNSTXPR auto mktime (std::time_t &result, std::tm &tm ) -> std::errc {
107+ MGUTILITY_CNSTXPR auto mktime (std::time_t &result, std::tm &time_struct ) -> std::errc {
108108 MGUTILITY_CNSTXPR std::array<std::array<uint32_t , 12 >, 2 > num_of_days{
109109 {{31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 ,
110110 31 }, // 365 days in a common year
@@ -114,30 +114,31 @@ MGUTILITY_CNSTXPR auto mktime(std::time_t &result, std::tm &tm) -> std::errc {
114114 result = 0 ;
115115
116116 // Check for out of range values in tm structure
117- if (tm .tm_mon > 12 || tm .tm_mon < 0 || tm .tm_mday > 31 || tm .tm_min > 60 ||
118- tm .tm_sec > 60 || tm .tm_hour > 24 ) {
117+ if (time_struct .tm_mon > 12 || time_struct .tm_mon < 0 || time_struct .tm_mday > 31 || time_struct .tm_min > 60 ||
118+ time_struct .tm_sec > 60 || time_struct .tm_hour > 24 ) {
119119 return std::errc::result_out_of_range;
120120 }
121121
122- tm .tm_year += 1900 ;
122+ time_struct .tm_year += 1900 ;
123123
124124 // Calculate the number of days since 1970
125- for (auto i{1970 }; i < tm .tm_year ; ++i) {
125+ for (auto i{1970 }; i < time_struct .tm_year ; ++i) {
126126 result += is_leap_year (i) ? 366 : 365 ;
127127 }
128128
129129 // Add the days for the current year
130- for (auto i{0 }; i < tm.tm_mon ; ++i) {
131- result += num_of_days[is_leap_year (tm.tm_year )][i];
130+ for (auto i{0 }; i < time_struct.tm_mon ; ++i) {
131+ // NOLINTNEXTLINE
132+ result += num_of_days[is_leap_year (time_struct.tm_year )][i];
132133 }
133134
134- result += tm .tm_mday - 1 ; // nth day since 1970
135+ result += time_struct .tm_mday - 1 ; // nth day since 1970
135136 result *= 24 ;
136- result += tm .tm_hour ;
137+ result += time_struct .tm_hour ;
137138 result *= 60 ;
138- result += tm .tm_min ;
139+ result += time_struct .tm_min ;
139140 result *= 60 ;
140- result += tm .tm_sec ;
141+ result += time_struct .tm_sec ;
141142
142143 return std::errc{};
143144}
@@ -148,56 +149,52 @@ MGUTILITY_CNSTXPR auto mktime(std::time_t &result, std::tm &tm) -> std::errc {
148149 * @param tm The tm structure to adjust.
149150 * @param offset The timezone offset in hours and minutes.
150151 */
151- MGUTILITY_CNSTXPR auto handle_timezone (tm &tm , int32_t offset) -> void {
152+ MGUTILITY_CNSTXPR auto handle_timezone (tm &time_struct , int32_t offset) -> void {
152153 const auto minute = offset % 100 ;
153154 const auto hour = offset / 100 ;
154155
155156 if (offset < 0 ) {
156- // Adjust minutes
157- if (tm.tm_min + minute < 0 ) {
158- tm.tm_min += 60 - minute;
159- tm.tm_hour -= 1 ;
160- if (tm.tm_hour < 0 ) {
161- tm.tm_hour += 24 ;
162- tm.tm_mday -= 1 ;
157+ if (time_struct.tm_min + minute < 0 ) {
158+ time_struct.tm_min += 60 - minute;
159+ time_struct.tm_hour -= 1 ;
160+ if (time_struct.tm_hour < 0 ) {
161+ time_struct.tm_hour += 24 ;
162+ time_struct.tm_mday -= 1 ;
163163 }
164164 } else {
165- tm .tm_min += minute;
165+ time_struct .tm_min += minute;
166166 }
167167
168- // Adjust hours
169- if (tm.tm_hour + hour < 0 ) {
170- tm.tm_hour += 24 + hour;
171- tm.tm_mday -= 1 ;
168+ if (time_struct.tm_hour + hour < 0 ) {
169+ time_struct.tm_hour += 24 + hour;
170+ time_struct.tm_mday -= 1 ;
172171 } else {
173- tm .tm_hour += hour;
172+ time_struct .tm_hour += hour;
174173 }
175174 } else {
176- // Adjust minutes
177- if (tm.tm_min + minute >= 60 ) {
178- tm.tm_min -= 60 - minute;
179- tm.tm_hour += 1 ;
180- if (tm.tm_hour >= 24 ) {
181- tm.tm_hour -= 24 ;
182- tm.tm_mday += 1 ;
175+ if (time_struct.tm_min + minute >= 60 ) {
176+ time_struct.tm_min -= 60 - minute;
177+ time_struct.tm_hour += 1 ;
178+ if (time_struct.tm_hour >= 24 ) {
179+ time_struct.tm_hour -= 24 ;
180+ time_struct.tm_mday += 1 ;
183181 }
184182 } else {
185- tm .tm_min += minute;
183+ time_struct .tm_min += minute;
186184 }
187185
188- // Adjust hours
189- if (tm.tm_hour + hour >= 24 ) {
190- tm.tm_hour += hour - 24 ;
191- tm.tm_mday += 1 ;
192- if (tm.tm_mon == 11 && tm.tm_mday > 31 ) { // Handle December overflow
193- tm.tm_mday = 1 ;
194- tm.tm_mon = 0 ;
195- } else if (tm.tm_mday > 30 ) { // Handle month overflow for other months
196- tm.tm_mday = 1 ;
197- tm.tm_mon += 1 ;
186+ if (time_struct.tm_hour + hour >= 24 ) {
187+ time_struct.tm_hour += hour - 24 ;
188+ time_struct.tm_mday += 1 ;
189+ if (time_struct.tm_mon == 11 && time_struct.tm_mday > 31 ) {
190+ time_struct.tm_mday = 1 ;
191+ time_struct.tm_mon = 0 ;
192+ } else if (time_struct.tm_mday > 30 ) {
193+ time_struct.tm_mday = 1 ;
194+ time_struct.tm_mon += 1 ;
198195 }
199196 } else {
200- tm .tm_hour += hour;
197+ time_struct .tm_hour += hour;
201198 }
202199 }
203200}
@@ -220,34 +217,49 @@ MGUTILITY_CNSTXPR auto parse_month(detail::tm &result, string_view date_str,
220217MGUTILITY_CNSTXPR auto parse_day (detail::tm &result, string_view date_str,
221218 uint32_t &next) -> std::errc {
222219 auto error = parse_integer (result.tm_mday , date_str, 2 , next);
220+ if (error != std::errc{}) {
221+ return error;
222+ }
223223 error = check_range (result.tm_mday , 1 , 31 );
224224 return error;
225225}
226226
227227MGUTILITY_CNSTXPR auto parse_hour (detail::tm &result, string_view date_str,
228228 uint32_t &next) -> std::errc {
229229 auto error = parse_integer (result.tm_hour , date_str, 2 , next);
230+ if (error != std::errc{}) {
231+ return error;
232+ }
230233 error = check_range (result.tm_hour , 0 , 23 );
231234 return error;
232235}
233236
234237MGUTILITY_CNSTXPR auto parse_minute (detail::tm &result, string_view date_str,
235238 uint32_t &next) -> std::errc {
236239 auto error = parse_integer (result.tm_min , date_str, 2 , next);
240+ if (error != std::errc{}) {
241+ return error;
242+ }
237243 error = check_range (result.tm_min , 0 , 59 );
238244 return error;
239245}
240246
241247MGUTILITY_CNSTXPR auto parse_second (detail::tm &result, string_view date_str,
242248 uint32_t &next) -> std::errc {
243249 auto error = parse_integer (result.tm_sec , date_str, 2 , next);
250+ if (error != std::errc{}) {
251+ return error;
252+ }
244253 error = check_range (result.tm_sec , 0 , 59 );
245254 return error;
246255}
247256
248257MGUTILITY_CNSTXPR auto parse_fraction (detail::tm &result, string_view date_str,
249258 uint32_t &next) -> std::errc {
250259 auto error = parse_integer (result.tm_ms , date_str, 3 , next);
260+ if (error != std::errc{}) {
261+ return error;
262+ }
251263 error = check_range (result.tm_ms , 0 , 999 );
252264 return error;
253265}
@@ -264,27 +276,25 @@ MGUTILITY_CNSTXPR auto parse_timezone_offset(detail::tm &result, string_view dat
264276 return std::errc::invalid_argument;
265277 }
266278
267- char sign = date_str[next++];
268- int32_t hour = 0 , minute = 0 ;
279+ const char sign = date_str[next++];
280+ int32_t hour = 0 ;
281+ int32_t minute = 0 ;
269282
270- // Parse hour part (2 digits)
271283 error = parse_integer (hour, date_str, 2 , next);
272284 if (error != std::errc{}) {
273285 return error;
274286 }
275287
276- // Optional colon
277288 if (next < date_str.size () && date_str[next] == ' :' ) {
278289 ++next;
279290 }
280291
281- // Parse minute part (2 digits)
282292 error = parse_integer (minute, date_str, 2 , next);
283293 if (error != std::errc{}) {
284294 return error;
285295 }
286296
287- int32_t offset = hour * 100 + minute;
297+ const int32_t offset = hour * 100 + minute;
288298 error = check_range (offset, 0 , 1200 );
289299 if (error != std::errc{}) {
290300 return error;
@@ -305,61 +315,64 @@ MGUTILITY_CNSTXPR auto parse_timezone_offset(detail::tm &result, string_view dat
305315 */
306316MGUTILITY_CNSTXPR auto get_time (detail::tm &result, string_view format,
307317 string_view date_str) -> std::errc {
308- int32_t count{0 };
309- std::size_t begin{0 }, end{0 };
310-
311- // Find the positions of format specifiers
312- begin = format.find (' {' );
313- end = format.find (' }' );
314- if (begin == string_view::npos || end == string_view::npos || begin >= end)
318+ const std::size_t begin = format.find (' {' );
319+ const std::size_t end = format.find (' }' );
320+ if (begin == string_view::npos || end == string_view::npos || begin >= end) {
315321 return std::errc::invalid_argument;
322+ }
316323
317- if (format[begin + 1 ] != ' :' || (end - begin < 3 || count != 0 ))
324+ if (format[begin + 1 ] != ' :' || (end - begin < 3 )) {
318325 return std::errc::invalid_argument;
326+ }
319327
320- uint32_t next{ 0 } ;
328+ uint32_t next = 0 ;
321329 std::errc error{};
322330
323- // Parse the date and time string based on the format specifiers
324- for (auto i{begin}; i < end; ++i) {
331+ for (std::size_t i = begin; i < end; ++i) {
325332 switch (format[i]) {
326333 case ' %' : {
327334 if (i + 1 >= format.size ()) {
328335 return std::errc::invalid_argument;
329336 }
330337 switch (format[i + 1 ]) {
331- case ' Y' : // Year with century (4 digits)
338+ case ' Y' :
332339 error = parse_year (result, date_str, next);
333340 break ;
334- case ' m' : // Month (01-12)
341+ case ' m' :
335342 error = parse_month (result, date_str, next);
336343 break ;
337- case ' d' : // Day of the month (01-31)
344+ case ' d' :
338345 error = parse_day (result, date_str, next);
339346 break ;
340- case ' F' : { // Full date (YYYY-MM-DD)
347+ case ' F' : {
348+ // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
341349 error = parse_year (result, date_str, next);
350+ // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
342351 error = parse_month (result, date_str, next);
352+ // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
343353 error = parse_day (result, date_str, next);
344354 } break ;
345- case ' H' : // Hour (00-23)
355+ case ' H' :
346356 error = parse_hour (result, date_str, next);
347357 break ;
348- case ' M' : // Minute (00-59)
358+ case ' M' :
349359 error = parse_minute (result, date_str, next);
350360 break ;
351- case ' S' : // Second (00-59)
361+ case ' S' :
352362 error = parse_second (result, date_str, next);
353363 break ;
354- case ' T' : { // Full time (HH:MM:SS)
364+ case ' T' : {
365+ // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
355366 error = parse_hour (result, date_str, next);
367+ // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
356368 error = parse_minute (result, date_str, next);
369+ // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
357370 error = parse_second (result, date_str, next);
358371 } break ;
359- case ' f' : // Milliseconds (000-999)
372+ case ' f' :
360373 error = parse_fraction (result, date_str, next);
361374 break ;
362- case ' z' : { // Timezone offset (+/-HHMM)
375+ case ' z' : {
363376 error = parse_timezone_offset (result, date_str, next);
364377 } break ;
365378 default :
@@ -399,18 +412,18 @@ MGUTILITY_CNSTXPR auto get_time(detail::tm &result, string_view format,
399412template <typename Clock = std::chrono::system_clock>
400413auto parse (typename Clock::time_point &time_point, string_view format,
401414 string_view date_str) -> std::error_code {
402- detail::tm tm {};
403- auto error = detail::get_time (tm , format, date_str);
415+ detail::tm time_struct {};
416+ auto error = detail::get_time (time_struct , format, date_str);
404417 if (error != std::errc{}) {
405418 return std::make_error_code (error);
406419 }
407420 std::time_t time_t {};
408- error = detail::mktime (time_t , tm );
421+ error = detail::mktime (time_t , time_struct );
409422 if (error != std::errc{}) {
410423 return std::make_error_code (error);
411424 }
412425 time_point = Clock::from_time_t (time_t );
413- time_point += std::chrono::milliseconds (tm .tm_ms );
426+ time_point += std::chrono::milliseconds (time_struct .tm_ms );
414427 return std::error_code{};
415428}
416429
0 commit comments