Improve `TimeSpan::fromString()`
* Avoid heap allocation and counting separators * Avoid code repetition * Throw an exception if too many parts are present (as the documentation suggests this function would do) * Allow emptry separators as this seems useful
This commit is contained in:
parent
8fb7de6fe0
commit
a425363eac
|
@ -41,26 +41,26 @@ TimeSpan TimeSpan::fromString(const char *str, char separator)
|
||||||
return TimeSpan();
|
return TimeSpan();
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<double> parts;
|
auto parts = std::array<double, 4>();
|
||||||
size_t partsSize = 1;
|
auto partsPresent = std::size_t();
|
||||||
for (const char *i = str; *i; ++i) {
|
for (const char *i = str;; ++i) {
|
||||||
*i == separator && ++partsSize;
|
if (*i != separator && *i != '\0') {
|
||||||
}
|
continue;
|
||||||
parts.reserve(partsSize);
|
}
|
||||||
|
if (partsPresent >= 4) {
|
||||||
for (const char *i = str;;) {
|
throw ConversionException("the time span specifications contains too many separators");
|
||||||
|
}
|
||||||
|
const auto part = std::string_view(str, i - str);
|
||||||
|
parts[partsPresent++] = part.empty() ? 0.0 : stringToNumber<double>(part);
|
||||||
if (*i == separator) {
|
if (*i == separator) {
|
||||||
parts.emplace_back(stringToNumber<double>(string(str, i)));
|
str = i + 1;
|
||||||
str = ++i;
|
}
|
||||||
} else if (*i == '\0') {
|
if (*i == '\0') {
|
||||||
parts.emplace_back(stringToNumber<double>(string(str, i)));
|
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (parts.size()) {
|
switch (partsPresent) {
|
||||||
case 1:
|
case 1:
|
||||||
return TimeSpan::fromSeconds(parts.front());
|
return TimeSpan::fromSeconds(parts.front());
|
||||||
case 2:
|
case 2:
|
||||||
|
|
|
@ -317,13 +317,17 @@ void ChronoTests::testDateTimeExpression()
|
||||||
*/
|
*/
|
||||||
void ChronoTests::testTimeSpan()
|
void ChronoTests::testTimeSpan()
|
||||||
{
|
{
|
||||||
// test fromString(...), this should also test all other from...() methods and + operator
|
// test various usages of fromString(...), all other from...() functions and the plus operator
|
||||||
CPPUNIT_ASSERT_EQUAL(TimeSpan(), TimeSpan::fromString(string()));
|
CPPUNIT_ASSERT_EQUAL(TimeSpan(), TimeSpan::fromString(string()));
|
||||||
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(5.0), TimeSpan::fromString("5.0"));
|
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(5.0), TimeSpan::fromString("5.0"));
|
||||||
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromMinutes(5.5), TimeSpan::fromString("5:30"));
|
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromMinutes(5.5), TimeSpan::fromString("5:30"));
|
||||||
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromHours(7.0) + TimeSpan::fromMinutes(5.5), TimeSpan::fromString("7:5:30"));
|
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromHours(7.0) + TimeSpan::fromMinutes(5.5), TimeSpan::fromString("7:5:30"));
|
||||||
|
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromDays(14.0), TimeSpan::fromString("14:::"));
|
||||||
|
CPPUNIT_ASSERT_THROW(TimeSpan::fromString("2:34a:53:32.5"), ConversionException);
|
||||||
|
CPPUNIT_ASSERT_THROW(TimeSpan::fromString("1:2:3:4:5"), ConversionException);
|
||||||
|
|
||||||
|
// test fromString(...) again and days(), hours(), ...
|
||||||
const auto test1 = TimeSpan::fromString("2:34:53:2.5");
|
const auto test1 = TimeSpan::fromString("2:34:53:2.5");
|
||||||
// test days(), hours(), ...
|
|
||||||
CPPUNIT_ASSERT_EQUAL(3, test1.days());
|
CPPUNIT_ASSERT_EQUAL(3, test1.days());
|
||||||
CPPUNIT_ASSERT_EQUAL(10, test1.hours());
|
CPPUNIT_ASSERT_EQUAL(10, test1.hours());
|
||||||
CPPUNIT_ASSERT_EQUAL(53, test1.minutes());
|
CPPUNIT_ASSERT_EQUAL(53, test1.minutes());
|
||||||
|
@ -332,12 +336,14 @@ void ChronoTests::testTimeSpan()
|
||||||
CPPUNIT_ASSERT(test1.totalDays() > 3.0 && test1.totalDays() < 4.0);
|
CPPUNIT_ASSERT(test1.totalDays() > 3.0 && test1.totalDays() < 4.0);
|
||||||
CPPUNIT_ASSERT(test1.totalHours() > (2 * 24 + 34) && test1.totalHours() < (2 * 24 + 35));
|
CPPUNIT_ASSERT(test1.totalHours() > (2 * 24 + 34) && test1.totalHours() < (2 * 24 + 35));
|
||||||
CPPUNIT_ASSERT(test1.totalMinutes() > (2 * 24 * 60 + 34 * 60 + 53) && test1.totalHours() < (2 * 24 * 60 + 34 * 60 + 54));
|
CPPUNIT_ASSERT(test1.totalMinutes() > (2 * 24 * 60 + 34 * 60 + 53) && test1.totalHours() < (2 * 24 * 60 + 34 * 60 + 54));
|
||||||
|
|
||||||
// test toString(...)
|
// test toString(...)
|
||||||
CPPUNIT_ASSERT_EQUAL("3 d 10 h 53 min 2 s 500 ms"s, test1.toString(TimeSpanOutputFormat::WithMeasures, false));
|
CPPUNIT_ASSERT_EQUAL("3 d 10 h 53 min 2 s 500 ms"s, test1.toString(TimeSpanOutputFormat::WithMeasures, false));
|
||||||
CPPUNIT_ASSERT_EQUAL("07:05:30"s, (TimeSpan::fromHours(7.0) + TimeSpan::fromMinutes(5.5)).toString());
|
CPPUNIT_ASSERT_EQUAL("07:05:30"s, (TimeSpan::fromHours(7.0) + TimeSpan::fromMinutes(5.5)).toString());
|
||||||
CPPUNIT_ASSERT_EQUAL("-5 s"s, TimeSpan::fromSeconds(-5.0).toString(TimeSpanOutputFormat::WithMeasures, false));
|
CPPUNIT_ASSERT_EQUAL("-5 s"s, TimeSpan::fromSeconds(-5.0).toString(TimeSpanOutputFormat::WithMeasures, false));
|
||||||
CPPUNIT_ASSERT_EQUAL("0 s"s, TimeSpan().toString(TimeSpanOutputFormat::WithMeasures, false));
|
CPPUNIT_ASSERT_EQUAL("0 s"s, TimeSpan().toString(TimeSpanOutputFormat::WithMeasures, false));
|
||||||
CPPUNIT_ASSERT_EQUAL("5e+02 µs"s, TimeSpan::fromMilliseconds(0.5).toString(TimeSpanOutputFormat::WithMeasures, false));
|
CPPUNIT_ASSERT_EQUAL("5e+02 µs"s, TimeSpan::fromMilliseconds(0.5).toString(TimeSpanOutputFormat::WithMeasures, false));
|
||||||
|
|
||||||
// test accuracy (of 100 nanoseconds)
|
// test accuracy (of 100 nanoseconds)
|
||||||
const auto test2 = TimeSpan::fromString("15.985077682");
|
const auto test2 = TimeSpan::fromString("15.985077682");
|
||||||
CPPUNIT_ASSERT_EQUAL(15.9850776, test2.totalSeconds());
|
CPPUNIT_ASSERT_EQUAL(15.9850776, test2.totalSeconds());
|
||||||
|
@ -348,9 +354,6 @@ void ChronoTests::testTimeSpan()
|
||||||
CPPUNIT_ASSERT_EQUAL("00:00:15.9850776"s, test2.toString());
|
CPPUNIT_ASSERT_EQUAL("00:00:15.9850776"s, test2.toString());
|
||||||
CPPUNIT_ASSERT_EQUAL("15 s 985 ms 77 µs 600 ns"s, test2.toString(TimeSpanOutputFormat::WithMeasures));
|
CPPUNIT_ASSERT_EQUAL("15 s 985 ms 77 µs 600 ns"s, test2.toString(TimeSpanOutputFormat::WithMeasures));
|
||||||
CPPUNIT_ASSERT_EQUAL("15.9850776"s, test2.toString(TimeSpanOutputFormat::TotalSeconds));
|
CPPUNIT_ASSERT_EQUAL("15.9850776"s, test2.toString(TimeSpanOutputFormat::TotalSeconds));
|
||||||
|
|
||||||
// test whether ConversionException() is thrown when invalid values are specified
|
|
||||||
CPPUNIT_ASSERT_THROW(TimeSpan::fromString("2:34a:53:32.5"), ConversionException);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
Loading…
Reference in New Issue