Add useful operators to `DateTimeExpression`

* Add serialization back to string, useful for implementing
  `TagValue::toString()`
* Add equality comparition
This commit is contained in:
Martchus 2022-08-13 14:10:40 +02:00
parent 4911d16129
commit 5acf422f71
3 changed files with 94 additions and 1 deletions

View File

@ -528,4 +528,78 @@ DateTimeExpression DateTimeExpression::fromString(const char *str)
return res;
}
/*!
* \brief Returns the string representation of the current instance in the ISO format.
* \remarks Only present parts will be included.
*/
std::string DateTimeExpression::toIsoString(char dateDelimiter, char timeDelimiter, char timeZoneDelimiter) const
{
auto s = std::stringstream(std::stringstream::in | std::stringstream::out);
s << setfill('0');
if (parts & DateTimeParts::Year) {
s << setw(4) << value.year();
}
if (parts & DateTimeParts::Month) {
if (s.tellp()) {
s << dateDelimiter;
}
s << setw(2) << value.month();
}
if (parts & DateTimeParts::Day) {
if (s.tellp()) {
s << dateDelimiter;
}
s << setw(2) << value.day();
}
if (parts & DateTimeParts::Hour) {
if (s.tellp()) {
s << 'T';
}
s << setw(2) << value.hour();
}
if (parts & DateTimeParts::Minute) {
if (s.tellp()) {
s << timeDelimiter;
}
s << setw(2) << value.minute();
}
if (parts & DateTimeParts::Second) {
if (s.tellp()) {
s << timeDelimiter;
}
s << setw(2) << value.second();
}
if (parts & DateTimeParts::SubSecond) {
const auto milli = value.millisecond();
const auto micro = value.microsecond();
const auto nano = value.nanosecond();
s << '.' << setw(3) << milli;
if (micro || nano) {
s << setw(3) << micro;
if (nano) {
s << nano / TimeSpan::nanosecondsPerTick;
}
}
}
if (parts & DateTimeParts::TimeZoneDelta) {
auto d = delta;
if (d.isNegative()) {
s << '-';
d = TimeSpan(-d.totalTicks());
} else {
s << '+';
}
if (parts & DateTimeParts::DeltaHour) {
s << setw(2) << d.hours();
}
if (parts & DateTimeParts::DeltaMinute) {
if (parts & DateTimeParts::DeltaHour) {
s << timeZoneDelimiter;
}
s << setw(2) << d.minutes();
}
}
return s.str();
}
} // namespace CppUtilities

View File

@ -166,6 +166,8 @@ struct CPP_UTILITIES_EXPORT DateTimeExpression {
DateTimeParts parts = DateTimeParts::None; /**< The parts present in the expression as flag enum. */
constexpr DateTime gmt() const;
constexpr bool operator==(const DateTimeExpression &other) const;
std::string toIsoString(char dateDelimiter = '-', char timeDelimiter = ':', char timeZoneDelimiter = ':') const;
static DateTimeExpression fromIsoString(const char *str);
static DateTimeExpression fromString(const char *str);
};
@ -178,6 +180,14 @@ constexpr DateTime DateTimeExpression::gmt() const
return value - delta;
}
/*!
* \brief Returns whether the expressions are equivalent.
*/
constexpr bool DateTimeExpression::operator==(const DateTimeExpression &other) const
{
return value == other.value && delta == other.delta && parts == other.parts;
}
/*!
* \brief Constructs a DateTime.
*/

View File

@ -230,44 +230,53 @@ void ChronoTests::testDateTime()
*/
void ChronoTests::testDateTimeExpression()
{
// check adding ISO timestamp parts one-by-one
// check adding ISO timestamp parts one-by-one and serialization back to string
auto expr = DateTimeExpression::fromIsoString("1");
auto parts = DateTimeParts::Year;
CPPUNIT_ASSERT_EQUAL(DateTime(), expr.value);
CPPUNIT_ASSERT_EQUAL(TimeSpan(), expr.delta);
CPPUNIT_ASSERT_EQUAL(parts, expr.parts);
CPPUNIT_ASSERT_EQUAL("0001"s, expr.toIsoString());
expr = DateTimeExpression::fromIsoString("1-1");
CPPUNIT_ASSERT_EQUAL(DateTime(), expr.value);
CPPUNIT_ASSERT_EQUAL(TimeSpan(), expr.delta);
CPPUNIT_ASSERT_EQUAL(parts |= DateTimeParts::Month, expr.parts);
CPPUNIT_ASSERT_EQUAL("0001-01"s, expr.toIsoString());
expr = DateTimeExpression::fromIsoString("1-1-1");
CPPUNIT_ASSERT_EQUAL(DateTime(), expr.value);
CPPUNIT_ASSERT_EQUAL(TimeSpan(), expr.delta);
CPPUNIT_ASSERT_EQUAL(parts |= DateTimeParts::Day, expr.parts);
CPPUNIT_ASSERT_EQUAL("0001-01-01"s, expr.toIsoString());
expr = DateTimeExpression::fromIsoString("1-1-1T0");
CPPUNIT_ASSERT_EQUAL(DateTime(), expr.value);
CPPUNIT_ASSERT_EQUAL(TimeSpan(), expr.delta);
CPPUNIT_ASSERT_EQUAL(parts |= DateTimeParts::Hour, expr.parts);
CPPUNIT_ASSERT_EQUAL("0001-01-01T00"s, expr.toIsoString());
expr = DateTimeExpression::fromIsoString("1-1-1T0:0");
CPPUNIT_ASSERT_EQUAL(DateTime(), expr.value);
CPPUNIT_ASSERT_EQUAL(TimeSpan(), expr.delta);
CPPUNIT_ASSERT_EQUAL(parts |= DateTimeParts::Minute, expr.parts);
CPPUNIT_ASSERT_EQUAL("0001-01-01T00:00"s, expr.toIsoString());
expr = DateTimeExpression::fromIsoString("1-1-1T0:0:0");
CPPUNIT_ASSERT_EQUAL(DateTime(), expr.value);
CPPUNIT_ASSERT_EQUAL(TimeSpan(), expr.delta);
CPPUNIT_ASSERT_EQUAL(parts |= DateTimeParts::Second, expr.parts);
CPPUNIT_ASSERT_EQUAL("0001-01-01T00:00:00"s, expr.toIsoString());
expr = DateTimeExpression::fromIsoString("1-1-1T0:0:0.0");
CPPUNIT_ASSERT_EQUAL(DateTime(), expr.value);
CPPUNIT_ASSERT_EQUAL(TimeSpan(), expr.delta);
CPPUNIT_ASSERT_EQUAL(parts |= DateTimeParts::SubSecond, expr.parts);
CPPUNIT_ASSERT_EQUAL("0001-01-01T00:00:00.000"s, expr.toIsoString());
expr = DateTimeExpression::fromIsoString("1-1-1T0:0:0.0+0");
CPPUNIT_ASSERT_EQUAL(DateTime(), expr.value);
CPPUNIT_ASSERT_EQUAL(TimeSpan(), expr.delta);
CPPUNIT_ASSERT_EQUAL(parts |= DateTimeParts::DeltaHour, expr.parts);
CPPUNIT_ASSERT_EQUAL("0001-01-01T00:00:00.000+00"s, expr.toIsoString());
expr = DateTimeExpression::fromIsoString("1-1-1T0:0:0.0-0:0");
CPPUNIT_ASSERT_EQUAL(DateTime(), expr.value);
CPPUNIT_ASSERT_EQUAL(TimeSpan(), expr.delta);
CPPUNIT_ASSERT_EQUAL(parts |= DateTimeParts::DeltaMinute, expr.parts);
CPPUNIT_ASSERT_EQUAL("0001-01-01T00:00:00.000+00:00"s, expr.toIsoString());
// check that omitting parts in the middle is not possible anyways
CPPUNIT_ASSERT_THROW(DateTimeExpression::fromIsoString("1-1T0"), ConversionException);