Skip to content

Commit 00fbbe8

Browse files
aylindmelloclaude
andcommitted
Preserve datetime precision and refactor array converters
Reader: handle fractional seconds by normalizing to 6 digits before parsing. Use MATLAB Pattern API throughout parseTOMLDatetime. Writer: use datevec (1 feval instead of 6) to extract datetime fields, fix ms/us decomposition, set subsecond_precision from actual data so whole-second datetimes omit the fractional part. Extract common reserve/loop/format pattern into buildArray template. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a2d0188 commit 00fbbe8

2 files changed

Lines changed: 87 additions & 91 deletions

File tree

‎cpp/mexfunctions/writetomlMex.cpp‎

Lines changed: 65 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,12 @@ class MexFunction : public matlab::mex::Function {
379379
tzStr = matlabStringToUtf8(tz.toUTF16());
380380
}
381381

382+
auto [ldt, precision] = extractLocalDatetime(val);
383+
382384
if (tzStr.empty()) {
383-
return toml::ordered_value(extractLocalDatetime(val));
385+
auto result = toml::ordered_value(ldt);
386+
result.as_local_datetime_fmt().subsecond_precision = precision;
387+
return result;
384388
}
385389

386390
matlab::data::Array offsetDuration = engine->feval(u"tzoffset", {val});
@@ -390,40 +394,59 @@ class MexFunction : public matlab::mex::Function {
390394
auto h = std::chrono::duration_cast<std::chrono::hours>(totalOffset);
391395
auto m = totalOffset - h;
392396

393-
toml::local_datetime ldt = extractLocalDatetime(val);
394-
return toml::ordered_value(toml::offset_datetime(
397+
auto result = toml::ordered_value(toml::offset_datetime(
395398
ldt.date, ldt.time,
396399
toml::time_offset(h.count(), std::abs(m.count()))));
400+
result.as_offset_datetime_fmt().subsecond_precision = precision;
401+
return result;
397402
}
398403

399-
toml::local_datetime extractLocalDatetime(
404+
std::pair<toml::local_datetime, std::size_t> extractLocalDatetime(
400405
const matlab::data::Array& val) {
401-
auto intField = [&](const char16_t* fn) -> int {
402-
matlab::data::TypedArray<double> r =
403-
engine->feval(fn, {val});
404-
return static_cast<int>(r[0]);
405-
};
406-
407-
int y = intField(u"year");
408-
int mo = intField(u"month");
409-
int d = intField(u"day");
410-
int h = intField(u"hour");
411-
int mi = intField(u"minute");
406+
matlab::data::TypedArray<double> dv =
407+
engine->feval(u"datevec", {val});
408+
int y = static_cast<int>(dv[0]);
409+
int mo = static_cast<int>(dv[1]);
410+
int d = static_cast<int>(dv[2]);
411+
int h = static_cast<int>(dv[3]);
412+
int mi = static_cast<int>(dv[4]);
413+
double sec = dv[5];
412414

413-
matlab::data::TypedArray<double> secArr =
414-
engine->feval(u"second", {val});
415-
double sec = secArr[0];
416415
int wholeSec = static_cast<int>(sec);
417-
int microseconds = static_cast<int>(
416+
int totalUs = static_cast<int>(
418417
std::round((sec - wholeSec) * 1e6));
419-
420-
return toml::local_datetime(
421-
toml::local_date(y, static_cast<toml::month_t>(mo - 1), d),
422-
toml::local_time(h, mi, wholeSec, microseconds * 1000, 0));
418+
int ms = totalUs / 1000;
419+
int us = totalUs % 1000;
420+
421+
std::size_t precision = 0;
422+
if (us != 0) precision = 6;
423+
else if (ms != 0) precision = 3;
424+
425+
return {
426+
toml::local_datetime(
427+
toml::local_date(y, static_cast<toml::month_t>(mo - 1), d),
428+
toml::local_time(h, mi, wholeSec, ms, us)),
429+
precision
430+
};
423431
}
424432

425433
// --- Array converters ---
426434

435+
template <typename Func>
436+
toml::ordered_value buildArray(size_t n, Func&& convertElem,
437+
bool isTableArray = false) {
438+
toml::ordered_array tomlArr;
439+
tomlArr.reserve(n);
440+
for (size_t i = 0; i < n; ++i) {
441+
tomlArr.push_back(convertElem(i));
442+
}
443+
toml::array_format_info fmt;
444+
fmt.body_indent = indentSize;
445+
fmt.fmt = isTableArray ? resolveTableArrayFormat(tomlArr)
446+
: resolveArrayFormat(tomlArr);
447+
return toml::ordered_value(std::move(tomlArr), fmt);
448+
}
449+
427450
template <typename T>
428451
toml::ordered_value convertIntType(const matlab::data::Array& val,
429452
size_t numel) {
@@ -439,97 +462,52 @@ class MexFunction : public matlab::mex::Function {
439462
toml::ordered_value convertNumericArray(
440463
const matlab::data::Array& val, size_t numel) {
441464
matlab::data::TypedArray<T> arr = val;
442-
toml::ordered_array tomlArr;
443-
tomlArr.reserve(numel);
444-
445-
for (auto elem : arr) {
465+
return buildArray(numel, [&](size_t i) -> toml::ordered_value {
446466
if constexpr (std::is_same_v<T, bool>) {
447-
tomlArr.push_back(toml::ordered_value(
448-
static_cast<bool>(elem)));
467+
return toml::ordered_value(static_cast<bool>(arr[i]));
449468
} else if constexpr (std::is_floating_point_v<T>) {
450-
tomlArr.push_back(convertDouble(
451-
static_cast<double>(elem)));
469+
return convertDouble(static_cast<double>(arr[i]));
452470
} else {
453-
tomlArr.push_back(toml::ordered_value(
454-
static_cast<toml::ordered_value::integer_type>(
455-
elem)));
471+
return toml::ordered_value(
472+
static_cast<toml::ordered_value::integer_type>(arr[i]));
456473
}
457-
}
458-
459-
toml::array_format_info fmt;
460-
fmt.fmt = resolveArrayFormat(tomlArr);
461-
fmt.body_indent = indentSize;
462-
return toml::ordered_value(std::move(tomlArr), fmt);
474+
});
463475
}
464476

465477
toml::ordered_value convertStringArray(
466478
const matlab::data::Array& val, size_t numel) {
467479
matlab::data::TypedArray<matlab::data::MATLABString> arr = val;
468-
toml::ordered_array tomlArr;
469-
tomlArr.reserve(numel);
470-
471480
toml::string_format_info strFmt;
472481
strFmt.fmt = resolveStringFormat();
473-
for (const auto& ms : arr) {
474-
std::string s = matlabStringToUtf8(ms);
475-
tomlArr.push_back(toml::ordered_value(std::move(s), strFmt));
476-
}
477-
478-
toml::array_format_info fmt;
479-
fmt.fmt = resolveArrayFormat(tomlArr);
480-
fmt.body_indent = indentSize;
481-
return toml::ordered_value(std::move(tomlArr), fmt);
482+
return buildArray(numel, [&](size_t i) {
483+
std::string s = matlabStringToUtf8(arr[i]);
484+
return toml::ordered_value(std::move(s), strFmt);
485+
});
482486
}
483487

484488
toml::ordered_value convertCellArray(
485489
const matlab::data::Array& val, size_t numel) {
486490
matlab::data::TypedArray<matlab::data::Array> cells = val;
487-
toml::ordered_array tomlArr;
488-
tomlArr.reserve(numel);
489-
490-
for (size_t i = 0; i < numel; ++i) {
491-
tomlArr.push_back(convert(cells[i]));
492-
}
493-
494-
toml::array_format_info fmt;
495-
fmt.fmt = resolveArrayFormat(tomlArr);
496-
fmt.body_indent = indentSize;
497-
return toml::ordered_value(std::move(tomlArr), fmt);
491+
return buildArray(numel, [&](size_t i) {
492+
return convert(cells[i]);
493+
});
498494
}
499495

500496
toml::ordered_value convertObjectArray(
501497
const matlab::data::Array& val, size_t numel) {
502498
matlab::data::TypedArray<matlab::data::Array> cells = val;
503-
504-
toml::ordered_array tomlArr;
505-
tomlArr.reserve(numel);
506-
507-
for (size_t i = 0; i < numel; ++i) {
508-
tomlArr.push_back(convertTable(cells[i]));
509-
}
510-
511-
toml::array_format_info fmt;
512-
fmt.body_indent = indentSize;
513-
fmt.fmt = resolveTableArrayFormat(tomlArr);
514-
return toml::ordered_value(std::move(tomlArr), fmt);
499+
return buildArray(numel, [&](size_t i) {
500+
return convertTable(cells[i]);
501+
}, true);
515502
}
516503

517504
toml::ordered_value convertDatetimeArray(
518505
const matlab::data::Array& val, size_t numel) {
519506
matlab::data::TypedArray<matlab::data::Array> cells =
520507
engine->feval(u"num2cell", {val});
521-
522-
toml::ordered_array tomlArr;
523-
tomlArr.reserve(numel);
524-
525-
for (size_t i = 0; i < numel; ++i) {
526-
tomlArr.push_back(convertDatetime(cells[i]));
527-
}
528-
529-
toml::array_format_info fmt;
530-
fmt.fmt = resolveArrayFormat(tomlArr);
531-
fmt.body_indent = indentSize;
532-
return toml::ordered_value(std::move(tomlArr), fmt);
508+
return buildArray(numel, [&](size_t i) {
509+
return convertDatetime(cells[i]);
510+
});
533511
}
534512

535513
// --- Format resolution ---

‎toolbox/+matlab/+io/+config/+internal/+read/expand.m‎

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,18 +146,36 @@
146146

147147
function dt = parseTOMLDatetime(str)
148148
offsetPat = characterListPattern("+-") + digitsPattern(2) + ":" + digitsPattern(2);
149+
hasFrac = contains(str, ".");
150+
if hasFrac
151+
str = normalizeFrac(str);
152+
end
149153
if contains(str, "T")
154+
baseFmt = "uuuu-MM-dd'T'HH:mm:ss";
155+
if hasFrac
156+
baseFmt = baseFmt + ".SSSSSS";
157+
end
150158
if endsWith(str, "Z")
151-
dt = datetime(str, InputFormat="uuuu-MM-dd'T'HH:mm:ssXXX", TimeZone="UTC");
159+
dt = datetime(str, InputFormat=baseFmt+"XXX", TimeZone="UTC");
152160
elseif endsWith(str, offsetPat)
153161
offset = extractAfter(str, strlength(str) - 6);
154-
dt = datetime(str, InputFormat="uuuu-MM-dd'T'HH:mm:ssXXX", TimeZone=offset);
162+
dt = datetime(str, InputFormat=baseFmt+"XXX", TimeZone=offset);
155163
else
156-
dt = datetime(str, InputFormat="uuuu-MM-dd'T'HH:mm:ss");
164+
dt = datetime(str, InputFormat=baseFmt);
157165
end
158166
elseif contains(str, ":")
159-
dt = datetime(str, InputFormat="HH:mm:ss");
167+
baseFmt = "HH:mm:ss";
168+
if hasFrac
169+
baseFmt = baseFmt + ".SSSSSS";
170+
end
171+
dt = datetime(str, InputFormat=baseFmt);
160172
else
161173
dt = datetime(str, InputFormat="uuuu-MM-dd");
162174
end
163175
end
176+
177+
function str = normalizeFrac(str)
178+
original = extract(str, "." + digitsPattern);
179+
normalized = extractBefore(pad(original, 7, 'right', '0'), 8);
180+
str = replace(str, original, normalized);
181+
end

0 commit comments

Comments
 (0)