2015-09-06 20:19:09 +02:00
|
|
|
#include "./timespan.h"
|
2015-04-22 18:36:40 +02:00
|
|
|
|
2015-09-06 20:19:09 +02:00
|
|
|
#include "../conversion/stringconversion.h"
|
2015-04-22 18:36:40 +02:00
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
#include <iomanip>
|
2017-05-01 03:13:11 +02:00
|
|
|
#include <sstream>
|
2015-04-22 18:36:40 +02:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
using namespace std;
|
2019-06-10 21:56:46 +02:00
|
|
|
|
|
|
|
namespace CppUtilities {
|
2015-04-22 18:36:40 +02:00
|
|
|
|
|
|
|
/*!
|
2019-06-10 21:56:46 +02:00
|
|
|
* \class TimeSpan
|
2015-04-22 18:36:40 +02:00
|
|
|
* \brief Represents a time interval.
|
2017-12-03 01:45:11 +01:00
|
|
|
*
|
|
|
|
* Note that the TimeSpan class is meant to express a time interval independently of the
|
|
|
|
* concrete starting DateTime and end DateTime and hence can not be expressed in years
|
|
|
|
* and month. For that use case, use the Period class instead.
|
|
|
|
*
|
2015-04-22 18:36:40 +02:00
|
|
|
* \remarks Time values are measured in 100-nanosecond units called ticks.
|
2017-12-03 01:45:11 +01:00
|
|
|
* \todo
|
|
|
|
* - Add method for parsing custom string formats.
|
|
|
|
* - Add method for printing to custom string formats.
|
2015-04-22 18:36:40 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
2016-08-30 19:59:04 +02:00
|
|
|
* \brief Parses the given C-style string as TimeSpan.
|
2017-12-03 01:45:11 +01:00
|
|
|
* \throws Throws a ConversionException if the specified \a str does not match the expected format.
|
|
|
|
*
|
|
|
|
* The expected format is "days:hours:minutes:seconds", eg. "5:31:4.521" for 5 hours, 31 minutes
|
|
|
|
* and 4.521 seconds. So parts at the front can be omitted and the parts can be fractions. The
|
|
|
|
* colon can be changed by specifying another \a separator.
|
2015-04-22 18:36:40 +02:00
|
|
|
*/
|
2016-08-30 19:59:04 +02:00
|
|
|
TimeSpan TimeSpan::fromString(const char *str, char separator)
|
2015-04-22 18:36:40 +02:00
|
|
|
{
|
2017-06-25 15:09:16 +02:00
|
|
|
if (!*str) {
|
|
|
|
return TimeSpan();
|
|
|
|
}
|
|
|
|
|
2015-04-22 18:36:40 +02:00
|
|
|
vector<double> parts;
|
2016-08-30 19:59:04 +02:00
|
|
|
size_t partsSize = 1;
|
2017-05-01 03:13:11 +02:00
|
|
|
for (const char *i = str; *i; ++i) {
|
2016-08-30 19:59:04 +02:00
|
|
|
*i == separator && ++partsSize;
|
|
|
|
}
|
|
|
|
parts.reserve(partsSize);
|
|
|
|
|
2017-05-01 03:13:11 +02:00
|
|
|
for (const char *i = str;;) {
|
|
|
|
if (*i == separator) {
|
2016-08-30 19:59:04 +02:00
|
|
|
parts.emplace_back(stringToNumber<double>(string(str, i)));
|
|
|
|
str = ++i;
|
2017-05-01 03:13:11 +02:00
|
|
|
} else if (*i == '\0') {
|
2016-08-30 19:59:04 +02:00
|
|
|
parts.emplace_back(stringToNumber<double>(string(str, i)));
|
2015-04-22 18:36:40 +02:00
|
|
|
break;
|
2016-08-30 19:59:04 +02:00
|
|
|
} else {
|
|
|
|
++i;
|
2016-01-27 00:14:20 +01:00
|
|
|
}
|
2015-04-22 18:36:40 +02:00
|
|
|
}
|
2016-08-30 19:59:04 +02:00
|
|
|
|
2017-05-01 03:13:11 +02:00
|
|
|
switch (parts.size()) {
|
2015-04-22 18:36:40 +02:00
|
|
|
case 1:
|
|
|
|
return TimeSpan::fromSeconds(parts.front());
|
|
|
|
case 2:
|
|
|
|
return TimeSpan::fromMinutes(parts.front()) + TimeSpan::fromSeconds(parts[1]);
|
|
|
|
case 3:
|
|
|
|
return TimeSpan::fromHours(parts.front()) + TimeSpan::fromMinutes(parts[1]) + TimeSpan::fromSeconds(parts[2]);
|
|
|
|
default:
|
|
|
|
return TimeSpan::fromDays(parts.front()) + TimeSpan::fromHours(parts[1]) + TimeSpan::fromMinutes(parts[2]) + TimeSpan::fromSeconds(parts[3]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2016-01-18 23:41:30 +01:00
|
|
|
* \brief Converts the value of the current TimeSpan object to its equivalent std::string representation
|
|
|
|
* according the given \a format.
|
2015-04-22 18:36:40 +02:00
|
|
|
*
|
2017-08-23 23:04:22 +02:00
|
|
|
* If \a fullSeconds is true the time interval will be rounded to full seconds.
|
2015-04-22 18:36:40 +02:00
|
|
|
*
|
|
|
|
* The string representation will be stored in \a result.
|
|
|
|
*/
|
2017-08-23 23:04:22 +02:00
|
|
|
void TimeSpan::toString(string &result, TimeSpanOutputFormat format, bool fullSeconds) const
|
2015-04-22 18:36:40 +02:00
|
|
|
{
|
|
|
|
stringstream s(stringstream::in | stringstream::out);
|
2017-06-25 15:09:16 +02:00
|
|
|
TimeSpan positive(m_ticks);
|
|
|
|
if (positive.isNegative()) {
|
|
|
|
s << '-';
|
|
|
|
positive.m_ticks = -positive.m_ticks;
|
|
|
|
}
|
2017-05-01 03:13:11 +02:00
|
|
|
switch (format) {
|
2015-04-22 18:36:40 +02:00
|
|
|
case TimeSpanOutputFormat::Normal:
|
2017-08-23 23:04:22 +02:00
|
|
|
s << setfill('0') << setw(2) << floor(positive.totalHours()) << ":" << setw(2) << positive.minutes() << ":" << setw(2) << positive.seconds();
|
|
|
|
if (!fullSeconds) {
|
|
|
|
const int milli(positive.milliseconds());
|
|
|
|
const int micro(positive.microseconds());
|
|
|
|
const int nano(positive.nanoseconds());
|
|
|
|
if (milli || micro || nano) {
|
|
|
|
s << '.' << setw(3) << milli;
|
|
|
|
if (micro || nano) {
|
|
|
|
s << setw(3) << micro;
|
|
|
|
if (nano) {
|
|
|
|
s << nano / TimeSpan::nanosecondsPerTick;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-04-22 18:36:40 +02:00
|
|
|
break;
|
|
|
|
case TimeSpanOutputFormat::WithMeasures:
|
2017-05-01 03:13:11 +02:00
|
|
|
if (isNull()) {
|
2017-08-23 23:04:22 +02:00
|
|
|
result = "0 s";
|
|
|
|
return;
|
2015-04-22 18:36:40 +02:00
|
|
|
} else {
|
2017-08-23 23:04:22 +02:00
|
|
|
if (!fullSeconds && positive.totalMilliseconds() < 1.0) {
|
|
|
|
s << setprecision(2) << positive.totalMicroseconds() << " µs";
|
2017-06-25 15:09:16 +02:00
|
|
|
} else {
|
2017-08-23 23:04:22 +02:00
|
|
|
bool needWhitespace = false;
|
2017-06-25 15:09:16 +02:00
|
|
|
if (const int days = positive.days()) {
|
2017-08-23 23:04:22 +02:00
|
|
|
needWhitespace = true;
|
|
|
|
s << days << " d";
|
2017-06-25 15:09:16 +02:00
|
|
|
}
|
|
|
|
if (const int hours = positive.hours()) {
|
2017-08-23 23:04:22 +02:00
|
|
|
if (needWhitespace)
|
|
|
|
s << ' ';
|
|
|
|
needWhitespace = true;
|
|
|
|
s << hours << " h";
|
2017-06-25 15:09:16 +02:00
|
|
|
}
|
|
|
|
if (const int minutes = positive.minutes()) {
|
2017-08-23 23:04:22 +02:00
|
|
|
if (needWhitespace)
|
|
|
|
s << ' ';
|
|
|
|
needWhitespace = true;
|
|
|
|
s << minutes << " min";
|
2017-06-25 15:09:16 +02:00
|
|
|
}
|
|
|
|
if (const int seconds = positive.seconds()) {
|
2017-08-23 23:04:22 +02:00
|
|
|
if (needWhitespace)
|
|
|
|
s << ' ';
|
|
|
|
needWhitespace = true;
|
|
|
|
s << seconds << " s";
|
2017-06-25 15:09:16 +02:00
|
|
|
}
|
2017-08-23 23:04:22 +02:00
|
|
|
if (!fullSeconds) {
|
2017-06-25 15:09:16 +02:00
|
|
|
if (const int milliseconds = positive.milliseconds()) {
|
2017-08-23 23:04:22 +02:00
|
|
|
if (needWhitespace)
|
|
|
|
s << ' ';
|
|
|
|
needWhitespace = true;
|
|
|
|
s << milliseconds << " ms";
|
|
|
|
}
|
|
|
|
if (const int microseconds = positive.microseconds()) {
|
|
|
|
if (needWhitespace)
|
|
|
|
s << ' ';
|
|
|
|
needWhitespace = true;
|
|
|
|
s << microseconds << " µs";
|
|
|
|
}
|
|
|
|
if (const int nanoseconds = positive.nanoseconds()) {
|
|
|
|
if (needWhitespace)
|
|
|
|
s << ' ';
|
|
|
|
s << nanoseconds << " ns";
|
2017-06-25 15:09:16 +02:00
|
|
|
}
|
|
|
|
}
|
2015-04-22 18:36:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2017-11-27 10:24:26 +01:00
|
|
|
case TimeSpanOutputFormat::TotalSeconds:
|
|
|
|
if (fullSeconds) {
|
|
|
|
s << setprecision(0);
|
2017-11-29 23:11:26 +01:00
|
|
|
} else {
|
|
|
|
s << setprecision(10);
|
2017-11-27 10:24:26 +01:00
|
|
|
}
|
|
|
|
s << positive.totalSeconds();
|
|
|
|
break;
|
2015-04-22 18:36:40 +02:00
|
|
|
}
|
2017-08-23 23:04:22 +02:00
|
|
|
result = s.str();
|
2015-04-22 18:36:40 +02:00
|
|
|
}
|
2019-06-10 21:56:46 +02:00
|
|
|
|
|
|
|
} // namespace CppUtilities
|