Support formatting ISO timestamps via DateTime::toString() with option to omit defaults

Omitting components are also allowed when parsing ISO timestamps so it makes sense to
have something similar in the other direction as well.

Note that the idea comes from ID3v2.4.0 which stores timestamps in a subset of ISO 8601
similarily to what this library supports and it allows to omit default components as
well.
charconv
Martchus 3 years ago
parent c834f8923d
commit 32780ed6a6
  1. 45
      chrono/datetime.cpp
  2. 4
      chrono/datetime.h
  3. 17
      tests/chronotests.cpp

@ -196,8 +196,53 @@ std::pair<DateTime, TimeSpan> DateTime::fromIsoString(const char *str)
*/
void DateTime::toString(string &result, DateTimeOutputFormat format, bool noMilliseconds) const
{
if (format == DateTimeOutputFormat::Iso) {
result = toIsoString();
return;
}
stringstream s(stringstream::in | stringstream::out);
s << setfill('0');
if (format == DateTimeOutputFormat::IsoOmittingDefaultComponents) {
constexpr auto dateDelimiter = '-', timeDelimiter = ':';
const int components[] = { year(), month(), day(), hour(), minute(), second(), millisecond(), microsecond(), nanosecond() };
const int *const firstTimeComponent = components + 3;
const int *const firstFractionalComponent = components + 6;
const int *const lastComponent = components + 8;
const int *componentsEnd = noMilliseconds ? firstFractionalComponent : lastComponent + 1;
for (const int *i = componentsEnd - 1; i > components; --i) {
if (i >= firstTimeComponent && *i == 0) {
componentsEnd = i;
} else if (i < firstTimeComponent && *i == 1) {
componentsEnd = i;
}
}
for (const int *i = components; i != componentsEnd; ++i) {
if (i == firstTimeComponent) {
s << 'T';
} else if (i == firstFractionalComponent) {
s << '.';
}
if (i == components) {
s << setw(4) << *i;
} else if (i < firstFractionalComponent) {
if (i < firstTimeComponent) {
s << dateDelimiter;
} else if (i > firstTimeComponent) {
s << timeDelimiter;
}
s << setw(2) << *i;
} else if (i < lastComponent) {
s << setw(3) << *i;
} else {
s << *i / TimeSpan::nanosecondsPerTick;
}
}
result = s.str();
return;
}
if (format == DateTimeOutputFormat::DateTimeAndWeekday || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
s << printDayOfWeek(dayOfWeek(), format == DateTimeOutputFormat::DateTimeAndShortWeekday) << ' ';
if (format == DateTimeOutputFormat::DateOnly || format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday

@ -19,7 +19,9 @@ enum class DateTimeOutputFormat {
DateOnly, /**< date only */
TimeOnly, /**< time only */
DateTimeAndWeekday, /**< date with weekday and time */
DateTimeAndShortWeekday /**< date with abbreviated weekday and time */
DateTimeAndShortWeekday, /**< date with abbreviated weekday and time */
Iso, /**< ISO format like DateTime::toIsoString() */
IsoOmittingDefaultComponents, /**< ISO format like DateTime::toIsoString() omitting default components, e.g. just "2017" instead of "2017-01-01T00:00:00" */
};
/*!

@ -187,6 +187,23 @@ void ChronoTests::testDateTime()
CPPUNIT_ASSERT_THROW_MESSAGE("invalid .", DateTime::fromIsoString("2017-08.5-23T19:40:15.985077682+02:00"), ConversionException);
CPPUNIT_ASSERT_THROW_MESSAGE("invalid :", DateTime::fromIsoString("2017:08-23T19:40:15.985077682+02:00"), ConversionException);
CPPUNIT_ASSERT_THROW_MESSAGE("invalid :", DateTime::fromIsoString("2017-08-23T19:40:15:985077682+02:00"), ConversionException);
// ISO string via toString() format option
CPPUNIT_ASSERT_EQUAL("1234-05-06T07:08:09.0105005"s, DateTime::fromDateAndTime(1234, 5, 6, 7, 8, 9, 10.5005).toString(DateTimeOutputFormat::Iso));
CPPUNIT_ASSERT_EQUAL("1234-05-06T07:08:09.0105005"s,
DateTime::fromDateAndTime(1234, 5, 6, 7, 8, 9, 10.5005).toString(DateTimeOutputFormat::IsoOmittingDefaultComponents));
CPPUNIT_ASSERT_EQUAL("1234-05-06T07:08:09.010500"s,
DateTime::fromDateAndTime(1234, 5, 6, 7, 8, 9, 10.500).toString(DateTimeOutputFormat::IsoOmittingDefaultComponents));
CPPUNIT_ASSERT_EQUAL(
"1234-05-06T07:08:09.010"s, DateTime::fromDateAndTime(1234, 5, 6, 7, 8, 9, 10).toString(DateTimeOutputFormat::IsoOmittingDefaultComponents));
CPPUNIT_ASSERT_EQUAL(
"1234-05-06T07:08:09"s, DateTime::fromDateAndTime(1234, 5, 6, 7, 8, 9).toString(DateTimeOutputFormat::IsoOmittingDefaultComponents));
CPPUNIT_ASSERT_EQUAL(
"1234-05-06T07:08"s, DateTime::fromDateAndTime(1234, 5, 6, 7, 8).toString(DateTimeOutputFormat::IsoOmittingDefaultComponents));
CPPUNIT_ASSERT_EQUAL("1234-05-06T07"s, DateTime::fromDateAndTime(1234, 5, 6, 7).toString(DateTimeOutputFormat::IsoOmittingDefaultComponents));
CPPUNIT_ASSERT_EQUAL("1234-05-06"s, DateTime::fromDateAndTime(1234, 5, 6).toString(DateTimeOutputFormat::IsoOmittingDefaultComponents));
CPPUNIT_ASSERT_EQUAL("1234-05"s, DateTime::fromDateAndTime(1234, 5).toString(DateTimeOutputFormat::IsoOmittingDefaultComponents));
CPPUNIT_ASSERT_EQUAL("1234"s, DateTime::fromDateAndTime(1234).toString(DateTimeOutputFormat::IsoOmittingDefaultComponents));
CPPUNIT_ASSERT_EQUAL("0001"s, DateTime().toString(DateTimeOutputFormat::IsoOmittingDefaultComponents));
// test now() and exactNow() (or at least whether both behave the same)
#if defined(PLATFORM_UNIX)

Loading…
Cancel
Save