Skip to content

Commit a9efe27

Browse files
authored
Refactor main function to improve error handling and parsing of chrono time (#8)
* Refactor main function to improve error handling and parsing of chrono time * Refactor parse functions to improve error handling and consistency in parameter naming * Refactor check_range and get_time functions for improved type safety and readability; update parse function to use consistent time_struct variable. * Add clang-format ignore comment for improved formatting control * Remove specific NOLINTNEXTLINE comment for improved readability in mktime function
1 parent 046f9a3 commit a9efe27

File tree

3 files changed

+103
-82
lines changed

3 files changed

+103
-82
lines changed

example/main.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
#include "mgutility/chrono/parse.hpp"
22

3+
// trunk-ignore-all(clang-format)
4+
35
// {fmt} for printing
46
#include <fmt/chrono.h>
57
#include <fmt/format.h>
68

79
int main() {
8-
const auto chrono_time =
9-
mgutility::chrono::parse("{:%FT%T.%f%z}", "2023-04-16T00:05:23.999+0100");
10+
auto chrono_time = std::chrono::system_clock::time_point{};
11+
const auto error =
12+
mgutility::chrono::parse(chrono_time, "{:%FT%T.%f%z}", "2023-04-16T00:05:23.999+0100");
13+
14+
if (error) {
15+
fmt::print("Error: {}\n", error.message());
16+
return 1;
17+
}
1018

1119
fmt::print("{:%F %T}\n", chrono_time); // prints 2023-04-15 23:05:23.999000000
1220
// ({fmt} trunk version)

include/mgutility/chrono/parse.hpp

Lines changed: 92 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@
3636
#include <stdexcept>
3737
#include <type_traits>
3838

39+
//NOLINTBEGIN(modernize-concat-nested-namespaces)
3940
namespace mgutility {
4041
namespace chrono {
4142
namespace 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>
6163
MGUTILITY_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,
220217
MGUTILITY_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

227227
MGUTILITY_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

234237
MGUTILITY_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

241247
MGUTILITY_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

248257
MGUTILITY_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
*/
306316
MGUTILITY_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,
399412
template <typename Clock = std::chrono::system_clock>
400413
auto 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

include/mgutility/std/charconv.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ constexpr auto char_to_int(char c) noexcept -> int {
6565
* @param value Reference to an integer where the parsed value will be stored.
6666
* @return from_chars_result The result of the parsing operation.
6767
*/
68-
template <typename T = int32_t>
68+
template <typename T = int32_t>
6969
MGUTILITY_CNSTXPR auto from_chars(const char *first, const char *last,
7070
T &value) noexcept -> from_chars_result {
7171
T result = 0;

0 commit comments

Comments
 (0)