From 4b4e8ba7ea5649fbdaed562557cf75f9cdb1229f Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Mon, 9 Nov 2020 17:50:05 -0800 Subject: [PATCH] Adding datetime format decimal and no decimal (#936) * revert change to xmlsoft link * remove link * Add DateFormat::Iso8601WithDecimals and DateFormat::Iso8601WithNoDecimals * changelog * add ToISO8601String with TimeFractionFormat * update the name and add extra test --- sdk/core/azure-core/CHANGELOG.md | 1 + .../azure-core/inc/azure/core/datetime.hpp | 49 ++++++++++++++++- sdk/core/azure-core/src/datetime.cpp | 13 +++-- sdk/core/azure-core/test/ut/datetime.cpp | 53 +++++++++++++++++++ 4 files changed, 109 insertions(+), 7 deletions(-) diff --git a/sdk/core/azure-core/CHANGELOG.md b/sdk/core/azure-core/CHANGELOG.md index 7ec7eae5a..35857a327 100644 --- a/sdk/core/azure-core/CHANGELOG.md +++ b/sdk/core/azure-core/CHANGELOG.md @@ -13,6 +13,7 @@ - Added `GetPort()` to `Url`. - Added `TransportPolicyOptions`. - Added `TelemetryPolicyOptions`. +- Added `DateFormat::ToIso8601String(TimeFractionFormat)`. ### Other changes and Improvements diff --git a/sdk/core/azure-core/inc/azure/core/datetime.hpp b/sdk/core/azure-core/inc/azure/core/datetime.hpp index a9aed5479..55a925e0b 100644 --- a/sdk/core/azure-core/inc/azure/core/datetime.hpp +++ b/sdk/core/azure-core/inc/azure/core/datetime.hpp @@ -25,6 +25,23 @@ namespace Azure { namespace Core { static constexpr IntervalType WindowsToPosixOffsetSeconds = 11644473600LL; public: + /** + * @brief Defines the format applied to the fraction part from any @DateFormat + * + */ + enum class TimeFractionFormat + { + /// Decimals are not included when there are no decimals in the source Datetime and any zeros + /// from the right are also removed. + DropTrailingZeros, + + /// Decimals are included for any Datetime. + AllDigits, + + /// Decimals are removed for any Datetime. + Truncate + }; + /** * @brief Defines the supported date and time string formats. */ @@ -34,7 +51,7 @@ namespace Azure { namespace Core { Rfc1123, /// ISO 8601. - Iso8601 + Iso8601, }; /** @@ -76,6 +93,19 @@ namespace Azure { namespace Core { std::string const& timeString, DateFormat format = DateFormat::Rfc1123); + private: + /** + * @brief Get a string representation of the @DateTime. + * + * @param format The representation format to use. + * @param fractionFormat The format for the fraction part of the Datetime. Only supported by + * ISO8601 + * + * @throw DateTimeException If year exceeds 9999, or if \p format is not recognized. + */ + std::string ToString(DateFormat format, TimeFractionFormat fractionFormat) const; + + public: /** * @brief Get a string representation of the @DateTime. * @@ -83,7 +113,22 @@ namespace Azure { namespace Core { * * @throw DateTimeException If year exceeds 9999, or if \p format is not recognized. */ - std::string ToString(DateFormat format = DateFormat::Rfc1123) const; + std::string ToString(DateFormat format = DateFormat::Rfc1123) const + { + return ToString(format, TimeFractionFormat::DropTrailingZeros); + }; + + /** + * @brief Get a string representation of the @DateTime formated with ISO8601. + * + * @param fractionFormat The format that is applied to the fraction part from the ISO8601 date. + * + * @throw DateTimeException If year exceeds 9999, or if \p fractionFormat is not recognized. + */ + std::string ToIso8601String(TimeFractionFormat fractionFormat) const + { + return ToString(DateFormat::Iso8601, fractionFormat); + }; /// Get the integral time value. IntervalType ToInterval() const { return m_interval; } diff --git a/sdk/core/azure-core/src/datetime.cpp b/sdk/core/azure-core/src/datetime.cpp index 313edc07b..6100eda70 100644 --- a/sdk/core/azure-core/src/datetime.cpp +++ b/sdk/core/azure-core/src/datetime.cpp @@ -127,7 +127,7 @@ constexpr char const monthNames[] = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep } // namespace -std::string DateTime::ToString(DateFormat format) const +std::string DateTime::ToString(DateFormat format, TimeFractionFormat fractionFormat) const { if (m_interval > 2650467743999999999LL) { @@ -209,7 +209,8 @@ std::string DateTime::ToString(DateFormat format) const leftover); outCursor += 19; - if (fracSec != 0) + if ((fracSec != 0 && fractionFormat != TimeFractionFormat::Truncate) + || fractionFormat == TimeFractionFormat::AllDigits) { // Append fractional second, which is a 7-digit value with no trailing zeros // This way, '1200' becomes '00012' @@ -226,11 +227,13 @@ std::string DateTime::ToString(DateFormat format) const ".%07d", fracSec); - while (outCursor[appended - 1] == '0') + if (fractionFormat != TimeFractionFormat::AllDigits) { - --appended; // trim trailing zeros + while (outCursor[appended - 1] == '0') + { + --appended; // trim trailing zeros + } } - outCursor += appended; } diff --git a/sdk/core/azure-core/test/ut/datetime.cpp b/sdk/core/azure-core/test/ut/datetime.cpp index 09a87fcc3..d4d241664 100644 --- a/sdk/core/azure-core/test/ut/datetime.cpp +++ b/sdk/core/azure-core/test/ut/datetime.cpp @@ -73,6 +73,59 @@ TEST(DateTime, ParseTimeRoundrip2) TestDateTimeRoundtrip("2013-11-19T14:30:59.1234567999Z", "2013-11-19T14:30:59.1234567Z"); } +TEST(DateTime, decimals) +{ + { + std::string strExpected("2020-10-13T21:06:15.3300000Z"); + auto dt = DateTime::FromString("2020-10-13T21:06:15.33Z", DateTime::DateFormat::Iso8601); + auto const str2 = dt.ToIso8601String(DateTime::TimeFractionFormat::AllDigits); + EXPECT_EQ(str2, strExpected); + } + + { + std::string strExpected("2020-10-13T21:06:15.0000000Z"); + auto dt = DateTime::FromString("2020-10-13T21:06:15Z", DateTime::DateFormat::Iso8601); + auto const str2 = dt.ToIso8601String(DateTime::TimeFractionFormat::AllDigits); + EXPECT_EQ(str2, strExpected); + } + + { + std::string strExpected("2020-10-13T21:06:15.1234500Z"); + auto dt = DateTime::FromString("2020-10-13T21:06:15.12345Z", DateTime::DateFormat::Iso8601); + auto const str2 = dt.ToIso8601String(DateTime::TimeFractionFormat::AllDigits); + EXPECT_EQ(str2, strExpected); + } +} + +TEST(DateTime, noDecimals) +{ + { + std::string strExpected("2020-10-13T21:06:15Z"); + auto dt = DateTime::FromString("2020-10-13T21:06:15Z", DateTime::DateFormat::Iso8601); + auto const str2 = dt.ToIso8601String(DateTime::TimeFractionFormat::Truncate); + EXPECT_EQ(str2, strExpected); + } + + { + std::string strExpected("2020-10-13T21:06:15Z"); + auto dt = DateTime::FromString("2020-10-13T21:06:15.99999Z", DateTime::DateFormat::Iso8601); + auto const str2 = dt.ToIso8601String(DateTime::TimeFractionFormat::Truncate); + EXPECT_EQ(str2, strExpected); + } +} + +TEST(DateTime, sameResultFromDefaultISO) +{ + { + auto dt = DateTime::FromString("2020-10-13T21:06:15.33000000Z", DateTime::DateFormat::Iso8601); + auto dt2 + = DateTime::FromString("2020-10-13T21:06:15.330000000Z", DateTime::DateFormat::Iso8601); + auto const str1 = dt.ToIso8601String(DateTime::TimeFractionFormat::DropTrailingZeros); + auto const str2 = dt2.ToString(DateTime::DateFormat::Iso8601); + EXPECT_EQ(str1, str2); + } +} + TEST(DateTime, ParseTimeRoundrip3) { // leading 0-s after the comma, tricky to parse correctly