diff options
author | Jacob Evans <jacob.evans@10gen.com> | 2020-04-01 22:06:09 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-01-29 13:38:48 +0000 |
commit | f952f10948d21b0fc588837d8937b0d04330c353 (patch) | |
tree | c0eb42b5aa6f05b19ef34e04e8d9d3c71b4e59e2 | |
parent | 56d986cef0d74231f6e1e8a05a6aaf6510716bab (diff) | |
download | mongo-f952f10948d21b0fc588837d8937b0d04330c353.tar.gz |
SERVER-47030 Fix date_time_support to not throw exceptions
(cherry picked from commit d169769bba283cd0a1906c81580ff8e7cf7bcd93)
-rw-r--r-- | src/mongo/db/exec/document_value/document_value_test.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/exec/document_value/value.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/query/datetime/date_time_support.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/query/datetime/date_time_support.h | 75 | ||||
-rw-r--r-- | src/mongo/db/query/datetime/date_time_support_test.cpp | 216 |
6 files changed, 207 insertions, 124 deletions
diff --git a/src/mongo/db/exec/document_value/document_value_test.cpp b/src/mongo/db/exec/document_value/document_value_test.cpp index 74c42cefe14..4ed34854dac 100644 --- a/src/mongo/db/exec/document_value/document_value_test.cpp +++ b/src/mongo/db/exec/document_value/document_value_test.cpp @@ -30,6 +30,7 @@ #define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kDefault #include <math.h> +#include <sstream> #include "mongo/platform/basic.h" @@ -2402,4 +2403,10 @@ public: OldStyleSuiteInitializer<All> myall; +TEST(ValueOutput, StreamOutputForIllegalDateProducesErrorToken) { + auto sout = std::ostringstream{}; + sout << mongo::Value{Date_t::min()}; + ASSERT_EQ("illegal date", sout.str()); +} + } // namespace DocumentTests diff --git a/src/mongo/db/exec/document_value/value.cpp b/src/mongo/db/exec/document_value/value.cpp index f0d9f684222..7afef48204d 100644 --- a/src/mongo/db/exec/document_value/value.cpp +++ b/src/mongo/db/exec/document_value/value.cpp @@ -57,6 +57,7 @@ using std::ostream; using std::string; using std::stringstream; using std::vector; +using namespace std::string_literals; constexpr StringData Value::kISOFormatString; @@ -634,7 +635,9 @@ string Value::coerceToString() const { return getTimestamp().toStringPretty(); case Date: - return TimeZoneDatabase::utcZone().formatDate(Value::kISOFormatString, getDate()); + return uassertStatusOKWithContext( + TimeZoneDatabase::utcZone().formatDate(Value::kISOFormatString, getDate()), + "failed while coercing date to string"); case EOO: case jstNULL: @@ -1170,8 +1173,14 @@ ostream& operator<<(ostream& out, const Value& val) { case Undefined: return out << "undefined"; case Date: - return out << TimeZoneDatabase::utcZone().formatDate(Value::kISOFormatString, - val.coerceToDate()); + return out << [&] { + if (auto string = TimeZoneDatabase::utcZone().formatDate(Value::kISOFormatString, + val.coerceToDate()); + string.isOK()) + return string.getValue(); + else + return "illegal date"s; + }(); case bsonTimestamp: return out << val.getTimestamp().toString(); case Object: diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 9534eebc108..4cee9ba5a3d 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -1876,10 +1876,12 @@ Value ExpressionDateToString::evaluate(const Document& root, Variables* variable return Value(BSONNULL); } - return Value(timeZone->formatDate(formatValue.getStringData(), date.coerceToDate())); + return Value(uassertStatusOK( + timeZone->formatDate(formatValue.getStringData(), date.coerceToDate()))); } - return Value(timeZone->formatDate(Value::kISOFormatString, date.coerceToDate())); + return Value( + uassertStatusOK(timeZone->formatDate(Value::kISOFormatString, date.coerceToDate()))); } void ExpressionDateToString::_doAddDependencies(DepsTracker* deps) const { @@ -5610,8 +5612,8 @@ public: }; table[BSONType::Date][BSONType::String] = [](const boost::intrusive_ptr<ExpressionContext>& expCtx, Value inputValue) { - auto dateString = TimeZoneDatabase::utcZone().formatDate(Value::kISOFormatString, - inputValue.getDate()); + auto dateString = uassertStatusOK(TimeZoneDatabase::utcZone().formatDate( + Value::kISOFormatString, inputValue.getDate())); return Value(dateString); }; table[BSONType::Date][BSONType::Bool] = diff --git a/src/mongo/db/query/datetime/date_time_support.cpp b/src/mongo/db/query/datetime/date_time_support.cpp index 5fedc644686..5bd16d798c9 100644 --- a/src/mongo/db/query/datetime/date_time_support.cpp +++ b/src/mongo/db/query/datetime/date_time_support.cpp @@ -560,9 +560,11 @@ void TimeZone::validateFromStringFormat(StringData format) { return validateFormat(format, kDateFromStringFormatMap); } -std::string TimeZone::formatDate(StringData format, Date_t date) const { +StatusWith<std::string> TimeZone::formatDate(StringData format, Date_t date) const { StringBuilder formatted; - outputDateWithFormat(formatted, format, date); - return formatted.str(); + if (auto status = outputDateWithFormat(formatted, format, date); status != Status::OK()) + return status; + else + return formatted.str(); } } // namespace mongo diff --git a/src/mongo/db/query/datetime/date_time_support.h b/src/mongo/db/query/datetime/date_time_support.h index be93ae1c9c4..c9f72f5ec58 100644 --- a/src/mongo/db/query/datetime/date_time_support.h +++ b/src/mongo/db/query/datetime/date_time_support.h @@ -31,7 +31,9 @@ #include <memory> #include <string> +#include <utility> +#include "mongo/base/status.h" #include "mongo/db/service_context.h" #include "mongo/util/string_map.h" #include "mongo/util/time_support.h" @@ -43,6 +45,8 @@ struct _timelib_tzinfo; namespace mongo { +using namespace std::string_literals; + /** * A TimeZone object represents one way of formatting/reading dates to compute things like the day * of the week or the hour of a given date. Different TimeZone objects may report different answers @@ -192,15 +196,15 @@ public: * Converts a date object to a string according to 'format'. 'format' can be any string literal, * containing 0 or more format specifiers like %Y (year) or %d (day of month). Callers must pass * a valid format string for 'format', i.e. one that has already been passed to - * validateFormat(). + * validateFormat(). May return a Status indicating that the date value is an unprintable range. */ - std::string formatDate(StringData format, Date_t) const; + StatusWith<std::string> formatDate(StringData format, Date_t) const; /** * Like formatDate, except outputs to an output stream like a std::ostream or a StringBuilder. */ template <typename OutputStream> - void outputDateWithFormat(OutputStream& os, StringData format, Date_t date) const { + auto outputDateWithFormat(OutputStream& os, StringData format, Date_t date) const { auto parts = dateParts(date); for (auto&& it = format.begin(); it != format.end(); ++it) { if (*it != '%') { @@ -217,51 +221,71 @@ public: break; case 'Y': // Year { - insertPadded(os, parts.year, 4); + if (auto status = insertPadded(os, parts.year, 4); status != Status::OK()) + return status; break; } case 'm': // Month - insertPadded(os, parts.month, 2); + if (auto status = insertPadded(os, parts.month, 2); status != Status::OK()) + return status; break; case 'd': // Day of month - insertPadded(os, parts.dayOfMonth, 2); + if (auto status = insertPadded(os, parts.dayOfMonth, 2); status != Status::OK()) + return status; break; case 'H': // Hour - insertPadded(os, parts.hour, 2); + if (auto status = insertPadded(os, parts.hour, 2); status != Status::OK()) + return status; break; case 'M': // Minute - insertPadded(os, parts.minute, 2); + if (auto status = insertPadded(os, parts.minute, 2); status != Status::OK()) + return status; break; case 'S': // Second - insertPadded(os, parts.second, 2); + if (auto status = insertPadded(os, parts.second, 2); status != Status::OK()) + return status; break; case 'L': // Millisecond - insertPadded(os, parts.millisecond, 3); + if (auto status = insertPadded(os, parts.millisecond, 3); + status != Status::OK()) + return status; break; case 'j': // Day of year - insertPadded(os, dayOfYear(date), 3); + if (auto status = insertPadded(os, dayOfYear(date), 3); status != Status::OK()) + return status; break; case 'w': // Day of week - insertPadded(os, dayOfWeek(date), 1); + if (auto status = insertPadded(os, dayOfWeek(date), 1); status != Status::OK()) + return status; break; case 'U': // Week - insertPadded(os, week(date), 2); + if (auto status = insertPadded(os, week(date), 2); status != Status::OK()) + return status; break; case 'G': // Iso year of week - insertPadded(os, isoYear(date), 4); + if (auto status = insertPadded(os, isoYear(date), 4); status != Status::OK()) + return status; break; case 'V': // Iso week - insertPadded(os, isoWeek(date), 2); + if (auto status = insertPadded(os, isoWeek(date), 2); status != Status::OK()) + return status; break; case 'u': // Iso day of week - insertPadded(os, isoDayOfWeek(date), 1); + if (auto status = insertPadded(os, isoDayOfWeek(date), 1); + status != Status::OK()) + return status; break; case 'z': // UTC offset as ±hhmm. { auto offset = utcOffset(date); - os << ((offset.count() < 0) ? "-" : "+"); // sign - insertPadded(os, std::abs(durationCount<Hours>(offset)), 2); // hh - insertPadded(os, std::abs(durationCount<Minutes>(offset)) % 60, 2); // mm + os << ((offset.count() < 0) ? "-" : "+"); // sign + if (auto status = insertPadded(os, std::abs(durationCount<Hours>(offset)), 2); + status != Status::OK()) // hh + return status; + if (auto status = + insertPadded(os, std::abs(durationCount<Minutes>(offset)) % 60, 2); + status != Status::OK()) // mm + return status; break; } case 'Z': // UTC offset in minutes. @@ -272,6 +296,7 @@ public: MONGO_UNREACHABLE; } } + return Status::OK(); } /** @@ -289,14 +314,15 @@ private: * count of number we simply insert the number without padding. */ template <typename OutputStream> - void insertPadded(OutputStream& os, int number, int width) const { + static auto insertPadded(OutputStream& os, int number, int width) { invariant(width >= 1); invariant(width <= 4); - uassert(18537, - str::stream() << "Could not convert date to string: date component was outside " - << "the supported range of 0-9999: " << number, - (number >= 0) && (number <= 9999)); + if ((number < 0) || (number > 9999)) + return Status{ErrorCodes::Error{18537}, + "Could not convert date to string: date component was outside " + "the supported range of 0-9999: "s + + std::to_string(number)}; int digits = 1; @@ -312,6 +338,7 @@ private: os.write("0000", width - digits); } os << number; + return Status::OK(); } struct TimelibTZInfoDeleter { diff --git a/src/mongo/db/query/datetime/date_time_support_test.cpp b/src/mongo/db/query/datetime/date_time_support_test.cpp index f11973aa145..fba52b3e029 100644 --- a/src/mongo/db/query/datetime/date_time_support_test.cpp +++ b/src/mongo/db/query/datetime/date_time_support_test.cpp @@ -387,12 +387,16 @@ TEST(NewYorkTimeBeforeEpoch, DoesComputeISOWeek) { TEST(UTCTimeBeforeEpoch, DoesFormatDate) { // Tuesday, Dec 30, 1969 13:42:23:211 auto date = Date_t::fromMillisSinceEpoch(-123456789LL); - ASSERT_EQ(TimeZoneDatabase::utcZone().formatDate("%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " - "dayOfWeek: %w, week: %U, isoYear: %G, " - "isoWeek: %V, isoDayOfWeek: %u, percent: %%", - date), - "1969/12/30 13:42:23:211, dayOfYear: 364, dayOfWeek: 3, week: 52, isoYear: 1970, " - "isoWeek: 01, isoDayOfWeek: 2, percent: %"); + auto result = TimeZoneDatabase::utcZone().formatDate( + "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " + "dayOfWeek: %w, week: %U, isoYear: %G, " + "isoWeek: %V, isoDayOfWeek: %u, percent: %%", + date); + ASSERT_OK(result); + ASSERT_EQ( + "1969/12/30 13:42:23:211, dayOfYear: 364, dayOfWeek: 3, week: 52, isoYear: 1970, " + "isoWeek: 01, isoDayOfWeek: 2, percent: %", + result.getValue()); } TEST(NewYorkTimeBeforeEpoch, DoesFormatDate) { @@ -400,23 +404,28 @@ TEST(NewYorkTimeBeforeEpoch, DoesFormatDate) { // Tuesday, Dec 30, 1969 13:42:23:211 auto date = Date_t::fromMillisSinceEpoch(-123456789LL); - ASSERT_EQ(newYorkZone.formatDate("%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " - "dayOfWeek: %w, week: %U, isoYear: %G, " - "isoWeek: %V, isoDayOfWeek: %u, percent: %%", - date), - "1969/12/30 08:42:23:211, dayOfYear: 364, dayOfWeek: 3, week: 52, isoYear: 1970, " - "isoWeek: 01, isoDayOfWeek: 2, percent: %"); + auto result = newYorkZone.formatDate( + "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " + "dayOfWeek: %w, week: %U, isoYear: %G, " + "isoWeek: %V, isoDayOfWeek: %u, percent: %%", + date); + ASSERT_OK(result); + ASSERT_EQ( + "1969/12/30 08:42:23:211, dayOfYear: 364, dayOfWeek: 3, week: 52, isoYear: 1970, " + "isoWeek: 01, isoDayOfWeek: 2, percent: %", + result.getValue()); } TEST(UTCTimeBeforeEpoch, DoesOutputFormatDate) { // Tuesday, Dec 30, 1969 13:42:23:211 auto date = Date_t::fromMillisSinceEpoch(-123456789LL); std::ostringstream os; - TimeZoneDatabase::utcZone().outputDateWithFormat(os, - "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " - "dayOfWeek: %w, week: %U, isoYear: %G, " - "isoWeek: %V, isoDayOfWeek: %u, percent: %%", - date); + ASSERT_OK(TimeZoneDatabase::utcZone().outputDateWithFormat( + os, + "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " + "dayOfWeek: %w, week: %U, isoYear: %G, " + "isoWeek: %V, isoDayOfWeek: %u, percent: %%", + date)); ASSERT_EQ(os.str(), "1969/12/30 13:42:23:211, dayOfYear: 364, dayOfWeek: 3, week: 52, isoYear: 1970, " "isoWeek: 01, isoDayOfWeek: 2, percent: %"); @@ -428,11 +437,11 @@ TEST(NewYorkTimeBeforeEpoch, DoesOutputFormatDate) { // Tuesday, Dec 30, 1969 13:42:23:211 auto date = Date_t::fromMillisSinceEpoch(-123456789LL); std::ostringstream os; - newYorkZone.outputDateWithFormat(os, - "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " - "dayOfWeek: %w, week: %U, isoYear: %G, " - "isoWeek: %V, isoDayOfWeek: %u, percent: %%", - date); + ASSERT_OK(newYorkZone.outputDateWithFormat(os, + "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " + "dayOfWeek: %w, week: %U, isoYear: %G, " + "isoWeek: %V, isoDayOfWeek: %u, percent: %%", + date)); ASSERT_EQ(os.str(), "1969/12/30 08:42:23:211, dayOfYear: 364, dayOfWeek: 3, week: 52, isoYear: 1970, " "isoWeek: 01, isoDayOfWeek: 2, percent: %"); @@ -567,12 +576,16 @@ TEST(NewYorkTimeAtEpoch, DoesComputeISOWeek) { TEST(UTCTimeAtEpoch, DoesFormatDate) { // Thu, Jan 1, 1970 00:00:00:000 auto date = Date_t::fromMillisSinceEpoch(0); - ASSERT_EQ(TimeZoneDatabase::utcZone().formatDate("%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " - "dayOfWeek: %w, week: %U, isoYear: %G, " - "isoWeek: %V, isoDayOfWeek: %u, percent: %%", - date), - "1970/01/01 00:00:00:000, dayOfYear: 001, dayOfWeek: 5, week: 00, isoYear: 1970, " - "isoWeek: 01, isoDayOfWeek: 4, percent: %"); + auto result = TimeZoneDatabase::utcZone().formatDate( + "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " + "dayOfWeek: %w, week: %U, isoYear: %G, " + "isoWeek: %V, isoDayOfWeek: %u, percent: %%", + date); + ASSERT_OK(result); + ASSERT_EQ( + "1970/01/01 00:00:00:000, dayOfYear: 001, dayOfWeek: 5, week: 00, isoYear: 1970, " + "isoWeek: 01, isoDayOfWeek: 4, percent: %", + result.getValue()); } TEST(NewYorkTimeAtEpoch, DoesFormatDate) { @@ -580,22 +593,27 @@ TEST(NewYorkTimeAtEpoch, DoesFormatDate) { // Thu, Jan 1, 1970 00:00:00:000Z auto date = Date_t::fromMillisSinceEpoch(0); - ASSERT_EQ(newYorkZone.formatDate("%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " - "dayOfWeek: %w, week: %U, isoYear: %G, " - "isoWeek: %V, isoDayOfWeek: %u, percent: %%", - date), - "1969/12/31 19:00:00:000, dayOfYear: 365, dayOfWeek: 4, week: 52, isoYear: 1970, " - "isoWeek: 01, isoDayOfWeek: 3, percent: %"); + auto result = newYorkZone.formatDate( + "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " + "dayOfWeek: %w, week: %U, isoYear: %G, " + "isoWeek: %V, isoDayOfWeek: %u, percent: %%", + date); + ASSERT_OK(result); + ASSERT_EQ( + "1969/12/31 19:00:00:000, dayOfYear: 365, dayOfWeek: 4, week: 52, isoYear: 1970, " + "isoWeek: 01, isoDayOfWeek: 3, percent: %", + result.getValue()); } TEST(UTCTimeAtEpoch, DoesOutputFormatDate) { auto date = Date_t::fromMillisSinceEpoch(0); std::ostringstream os; - TimeZoneDatabase::utcZone().outputDateWithFormat(os, - "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " - "dayOfWeek: %w, week: %U, isoYear: %G, " - "isoWeek: %V, isoDayOfWeek: %u, percent: %%", - date); + ASSERT_OK(TimeZoneDatabase::utcZone().outputDateWithFormat( + os, + "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " + "dayOfWeek: %w, week: %U, isoYear: %G, " + "isoWeek: %V, isoDayOfWeek: %u, percent: %%", + date)); ASSERT_EQ(os.str(), "1970/01/01 00:00:00:000, dayOfYear: 001, dayOfWeek: 5, week: 00, isoYear: 1970, " "isoWeek: 01, isoDayOfWeek: 4, percent: %"); @@ -605,11 +623,11 @@ TEST(NewYorkTimeAtEpoch, DoesOutputFormatDate) { auto newYorkZone = kDefaultTimeZoneDatabase.getTimeZone("America/New_York"); auto date = Date_t::fromMillisSinceEpoch(0); std::ostringstream os; - newYorkZone.outputDateWithFormat(os, - "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " - "dayOfWeek: %w, week: %U, isoYear: %G, " - "isoWeek: %V, isoDayOfWeek: %u, percent: %%", - date); + ASSERT_OK(newYorkZone.outputDateWithFormat(os, + "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " + "dayOfWeek: %w, week: %U, isoYear: %G, " + "isoWeek: %V, isoDayOfWeek: %u, percent: %%", + date)); ASSERT_EQ(os.str(), "1969/12/31 19:00:00:000, dayOfYear: 365, dayOfWeek: 4, week: 52, isoYear: 1970, " "isoWeek: 01, isoDayOfWeek: 3, percent: %"); @@ -879,47 +897,60 @@ TEST(NewYorkTimeAfterEpoch, DoesComputeISOWeek) { TEST(UTCTimeAfterEpoch, DoesFormatDate) { // Tue, Jun 6, 2017 19:38:43:234. auto date = Date_t::fromMillisSinceEpoch(1496777923234LL); - ASSERT_EQ(TimeZoneDatabase::utcZone().formatDate("%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " - "dayOfWeek: %w, week: %U, isoYear: %G, " - "isoWeek: %V, isoDayOfWeek: %u, percent: %%", - date), - "2017/06/06 19:38:43:234, dayOfYear: 157, dayOfWeek: 3, week: 23, isoYear: 2017, " - "isoWeek: 23, isoDayOfWeek: 2, percent: %"); + auto result = TimeZoneDatabase::utcZone().formatDate( + "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " + "dayOfWeek: %w, week: %U, isoYear: %G, " + "isoWeek: %V, isoDayOfWeek: %u, percent: %%", + date); + ASSERT_OK(result); + ASSERT_EQ( + "2017/06/06 19:38:43:234, dayOfYear: 157, dayOfWeek: 3, week: 23, isoYear: 2017, " + "isoWeek: 23, isoDayOfWeek: 2, percent: %", + result.getValue()); } TEST(NewYorkTimeAfterEpoch, DoesFormatDate) { // 2017-06-06T19:38:43:234Z (Tuesday). auto date = Date_t::fromMillisSinceEpoch(1496777923234LL); auto newYorkZone = kDefaultTimeZoneDatabase.getTimeZone("America/New_York"); - ASSERT_EQ(newYorkZone.formatDate("%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " - "dayOfWeek: %w, week: %U, isoYear: %G, " - "isoWeek: %V, isoDayOfWeek: %u, percent: %%", - date), - "2017/06/06 15:38:43:234, dayOfYear: 157, dayOfWeek: 3, week: 23, isoYear: 2017, " - "isoWeek: 23, isoDayOfWeek: 2, percent: %"); + auto result = newYorkZone.formatDate( + "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " + "dayOfWeek: %w, week: %U, isoYear: %G, " + "isoWeek: %V, isoDayOfWeek: %u, percent: %%", + date); + ASSERT_OK(result); + ASSERT_EQ( + "2017/06/06 15:38:43:234, dayOfYear: 157, dayOfWeek: 3, week: 23, isoYear: 2017, " + "isoWeek: 23, isoDayOfWeek: 2, percent: %", + result.getValue()); } TEST(UTCOffsetAfterEpoch, DoesFormatDate) { // 2017-06-06T19:38:43:234Z (Tuesday). auto date = Date_t::fromMillisSinceEpoch(1496777923234LL); auto offsetSpec = kDefaultTimeZoneDatabase.getTimeZone("+02:30"); - ASSERT_EQ(offsetSpec.formatDate("%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " - "dayOfWeek: %w, week: %U, isoYear: %G, " - "isoWeek: %V, isoDayOfWeek: %u, percent: %%", - date), - "2017/06/06 22:08:43:234, dayOfYear: 157, dayOfWeek: 3, week: 23, isoYear: 2017, " - "isoWeek: 23, isoDayOfWeek: 2, percent: %"); + auto result = offsetSpec.formatDate( + "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " + "dayOfWeek: %w, week: %U, isoYear: %G, " + "isoWeek: %V, isoDayOfWeek: %u, percent: %%", + date); + ASSERT_OK(result); + ASSERT_EQ( + "2017/06/06 22:08:43:234, dayOfYear: 157, dayOfWeek: 3, week: 23, isoYear: 2017, " + "isoWeek: 23, isoDayOfWeek: 2, percent: %", + result.getValue()); } TEST(UTCTimeAfterEpoch, DoesOutputFormatDate) { // Tue, Jun 6, 2017 19:38:43:234. auto date = Date_t::fromMillisSinceEpoch(1496777923234LL); std::ostringstream os; - TimeZoneDatabase::utcZone().outputDateWithFormat(os, - "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " - "dayOfWeek: %w, week: %U, isoYear: %G, " - "isoWeek: %V, isoDayOfWeek: %u, percent: %%", - date); + ASSERT_OK(TimeZoneDatabase::utcZone().outputDateWithFormat( + os, + "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " + "dayOfWeek: %w, week: %U, isoYear: %G, " + "isoWeek: %V, isoDayOfWeek: %u, percent: %%", + date)); ASSERT_EQ(os.str(), "2017/06/06 19:38:43:234, dayOfYear: 157, dayOfWeek: 3, week: 23, isoYear: 2017, " "isoWeek: 23, isoDayOfWeek: 2, percent: %"); @@ -930,11 +961,11 @@ TEST(NewYorkTimeAfterEpoch, DoesOutputFormatDate) { auto date = Date_t::fromMillisSinceEpoch(1496777923234LL); std::ostringstream os; auto newYorkZone = kDefaultTimeZoneDatabase.getTimeZone("America/New_York"); - newYorkZone.outputDateWithFormat(os, - "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " - "dayOfWeek: %w, week: %U, isoYear: %G, " - "isoWeek: %V, isoDayOfWeek: %u, percent: %%", - date); + ASSERT_OK(newYorkZone.outputDateWithFormat(os, + "%Y/%m/%d %H:%M:%S:%L, dayOfYear: %j, " + "dayOfWeek: %w, week: %U, isoYear: %G, " + "isoWeek: %V, isoDayOfWeek: %u, percent: %%", + date)); ASSERT_EQ(os.str(), "2017/06/06 15:38:43:234, dayOfYear: 157, dayOfWeek: 3, week: 23, isoYear: 2017, " "isoWeek: 23, isoDayOfWeek: 2, percent: %"); @@ -965,37 +996,42 @@ TEST(DateFormat, ThrowsUserExceptionIfGivenUnmatchedPercent) { 18535); } -TEST(DateFormat, ThrowsUserExceptionIfGivenDateBeforeYear0) { +TEST(DateFormat, ProducesNonOKStatusGivenDateBeforeYear0) { const long long kMillisPerYear = 31556926000; - ASSERT_THROWS_CODE(TimeZoneDatabase::utcZone().formatDate( - "%Y", Date_t::fromMillisSinceEpoch(-(kMillisPerYear * 1971))), - AssertionException, - 18537); + ASSERT_EQ(18537, + TimeZoneDatabase::utcZone() + .formatDate("%Y", Date_t::fromMillisSinceEpoch(-(kMillisPerYear * 1971))) + .getStatus() + .code()); - ASSERT_THROWS_CODE(TimeZoneDatabase::utcZone().formatDate( - "%G", Date_t::fromMillisSinceEpoch(-(kMillisPerYear * 1971))), - AssertionException, - 18537); + ASSERT_EQ(18537, + TimeZoneDatabase::utcZone() + .formatDate("%G", Date_t::fromMillisSinceEpoch(-(kMillisPerYear * 1971))) + .getStatus() + .code()); - ASSERT_EQ("0000", - TimeZoneDatabase::utcZone().formatDate( - "%Y", Date_t::fromMillisSinceEpoch(-(kMillisPerYear * 1970)))); + auto result = TimeZoneDatabase::utcZone().formatDate( + "%Y", Date_t::fromMillisSinceEpoch(-(kMillisPerYear * 1970))); + ASSERT_OK(result); + ASSERT_EQ("0000", result.getValue()); } -TEST(DateFormat, ThrowsUserExceptionIfGivenDateAfterYear9999) { - ASSERT_THROWS_CODE( - TimeZoneDatabase::utcZone().formatDate("%Y", Date_t::max()), AssertionException, 18537); +TEST(DateFormat, ProducesNonOKStatusIfGivenDateAfterYear9999) { + ASSERT_EQ(18537, + TimeZoneDatabase::utcZone().formatDate("%Y", Date_t::max()).getStatus().code()); - ASSERT_THROWS_CODE( - TimeZoneDatabase::utcZone().formatDate("%G", Date_t::max()), AssertionException, 18537); + ASSERT_EQ(18537, + TimeZoneDatabase::utcZone().formatDate("%G", Date_t::max()).getStatus().code()); } TEST(DateFromString, CorrectlyParsesStringThatMatchesFormat) { auto input = "2017-07-04T10:56:02Z"; auto format = "%Y-%m-%dT%H:%M:%SZ"_sd; auto date = kDefaultTimeZoneDatabase.fromString(input, kDefaultTimeZone, format); - ASSERT_EQ(TimeZoneDatabase::utcZone().formatDate(format, date), input); + auto result = TimeZoneDatabase::utcZone().formatDate(format, date); + ASSERT_OK(result); + ASSERT_EQ(input, result.getValue()); } TEST(DateFromString, RejectsStringWithInvalidYearFormat) { |