summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorBilly Donahue <billy.donahue@mongodb.com>2020-04-02 13:49:54 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-04-21 07:40:44 +0000
commit10ee8c156f747c55895067cb418827c57c59557b (patch)
tree9d10144b571ff253895802656e9cde490470e84a /src/mongo
parent79cfaa04168ce3994b5499f45fd728ebccbef0bb (diff)
downloadmongo-10ee8c156f747c55895067cb418827c57c59557b.tar.gz
SERVER-47220 log json field padding
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/bson/generator_extended_relaxed_2_0_0.h5
-rw-r--r--src/mongo/logv2/json_formatter.cpp199
-rw-r--r--src/mongo/logv2/text_formatter.cpp16
-rw-r--r--src/mongo/util/time_support.cpp178
-rw-r--r--src/mongo/util/time_support.h80
5 files changed, 228 insertions, 250 deletions
diff --git a/src/mongo/bson/generator_extended_relaxed_2_0_0.h b/src/mongo/bson/generator_extended_relaxed_2_0_0.h
index 4b18082d33e..c310e163090 100644
--- a/src/mongo/bson/generator_extended_relaxed_2_0_0.h
+++ b/src/mongo/bson/generator_extended_relaxed_2_0_0.h
@@ -82,10 +82,7 @@ public:
// Date_t::millis is negative (before the epoch).
if (val.isFormattable()) {
appendTo(buffer, R"({"$date":")"_sd);
- if (_localDate)
- outputDateAsISOStringLocal(buffer, val);
- else
- outputDateAsISOStringUTC(buffer, val);
+ appendTo(buffer, StringData{DateStringBuffer{}.iso8601(val, _localDate)});
appendTo(buffer, R"("})");
} else {
ExtendedCanonicalV200Generator::writeDate(buffer, val);
diff --git a/src/mongo/logv2/json_formatter.cpp b/src/mongo/logv2/json_formatter.cpp
index df87a5b40a8..98df9bb898c 100644
--- a/src/mongo/logv2/json_formatter.cpp
+++ b/src/mongo/logv2/json_formatter.cpp
@@ -230,109 +230,116 @@ void JSONFormatter::format(fmt::memory_buffer& buffer,
const TypeErasedAttributeStorage& attrs,
LogTag tags,
LogTruncation truncation) const {
+ namespace c = constants;
+ static constexpr auto kFmt = JsonStringFormat::ExtendedRelaxedV2_0_0;
StringData severityString = severity.toStringDataCompact();
StringData componentString = component.getNameForLog();
-
- // Put all fields up until the message value
- static const auto& fmtStrOpen = *new auto(fmt::compile<StringData>(R"({{)"
- R"("{}":{{"$date":")"));
- compiled_format_to(buffer, fmtStrOpen, constants::kTimestampFieldName);
- switch (_timestampFormat) {
- case LogTimestampFormat::kISO8601UTC:
- outputDateAsISOStringUTC(buffer, date);
- break;
- case LogTimestampFormat::kISO8601Local:
- outputDateAsISOStringLocal(buffer, date);
- break;
+ bool local = _timestampFormat == LogTimestampFormat::kISO8601Local;
+ size_t attributeMaxSize = truncation != LogTruncation::Enabled
+ ? 0
+ : (_maxAttributeSizeKB != 0 ? _maxAttributeSizeKB->loadRelaxed() * 1024
+ : c::kDefaultMaxAttributeOutputSizeKB * 1024);
+ auto write = [&](StringData s) { buffer.append(s.rawData(), s.rawData() + s.size()); };
+
+ struct CommaTracker {
+ StringData comma;
+ size_t padDebt = 0;
};
- static const auto& fmtStrBody =
- *new auto(fmt::compile<StringData, // severity start
- StringData,
- StringData,
- int,
- StringData, // component start
- StringData,
- StringData,
- int,
- StringData, // id start
- StringData,
- StringData,
- int,
- StringData, // context start
- StringData,
- StringData> // message start
- (R"("}},)" // close timestamp
- R"("{}":"{}"{: <{}})" // severity with padding for the comma
- R"("{}":"{}"{: <{}})" // component with padding for the comma
- R"("{}":{}{: <{}})" // id with padding for the comma
- R"("{}":"{}",)" // context
- R"("{}":")" // message
- ));
- fmt::format_int idString(id);
- compiled_format_to(
- buffer,
- fmtStrBody,
- // severity, left align the comma and add padding to create fixed column width
- constants::kSeverityFieldName,
- severityString,
- ","_sd,
- 3 - severityString.size(),
- // component, left align the comma and add padding to create fixed column width
- constants::kComponentFieldName,
- componentString,
- ","_sd,
- 9 - componentString.size(),
- // id
- constants::kIdFieldName,
- StringData(idString.data(), idString.size()),
- ","_sd,
- 8 - idString.size(),
- // context
- constants::kContextFieldName,
- context,
- // message
- constants::kMessageFieldName);
-
- str::escapeForJSON(buffer, message);
- buffer.push_back('"');
-
- static const auto& fmtStrAttr = *new auto(fmt::compile<StringData>(R"(,"{}":{{)"));
- static const auto& fmtStrTruncated = *new auto(fmt::compile<StringData>(R"(,"{}":)"));
- if (!attrs.empty()) {
- compiled_format_to(buffer, fmtStrAttr, constants::kAttributesFieldName);
- // comma separated list of attributes (no opening/closing brace are added here)
- size_t attributeMaxSize = 0;
- if (truncation == LogTruncation::Enabled) {
- if (_maxAttributeSizeKB)
- attributeMaxSize = _maxAttributeSizeKB->loadRelaxed() * 1024;
- else
- attributeMaxSize = constants::kDefaultMaxAttributeOutputSizeKB * 1024;
- }
- JSONValueExtractor extractor(buffer, attributeMaxSize);
- attrs.apply(extractor);
- buffer.push_back('}');
-
- if (BSONObj truncated = extractor.truncated(); !truncated.isEmpty()) {
- compiled_format_to(buffer, fmtStrTruncated, constants::kTruncatedFieldName);
- truncated.jsonStringBuffer(
- JsonStringFormat::ExtendedRelaxedV2_0_0, 0, false, buffer, 0);
- }
- if (BSONObj truncatedSizes = extractor.truncatedSizes(); !truncatedSizes.isEmpty()) {
- compiled_format_to(buffer, fmtStrTruncated, constants::kTruncatedSizeFieldName);
- truncatedSizes.jsonStringBuffer(
- JsonStringFormat::ExtendedRelaxedV2_0_0, 0, false, buffer, 0);
+ // First call to `comma` emits nothing, subsequent calls emit ",", so this is a
+ // prefix used to join elements. If there is any padding debt in the
+ // tracker, then that amount of whitespace is emitted after the comma and the
+ // debt is cleared.
+ auto writeComma = [&](CommaTracker& tracker) {
+ write(tracker.comma);
+ tracker.comma = ","_sd;
+ while (tracker.padDebt) {
+ // Relies on substr's truncation.
+ StringData space = " "_sd.substr(0, tracker.padDebt);
+ write(space);
+ tracker.padDebt -= space.size();
}
- }
+ };
- static const auto& fmtStrTags = *new auto(fmt::compile<StringData>(R"(,"{}":)"));
- if (tags != LogTag::kNone) {
- compiled_format_to(buffer, fmtStrTags, constants::kTagsFieldName);
- tags.toBSONArray().jsonStringBuffer(
- JsonStringFormat::ExtendedRelaxedV2_0_0, 0, true, buffer);
- }
+ // Emit `f()` normally, noting its size. If it is narrower than `width`, we incur a
+ // padding debt which is stored in the `tracker`. The next `comma` emitted by the
+ // `tracker` will be followed by enough whitespace to pay off the debt.
+ auto padNextComma = [&](CommaTracker& tracker, size_t width, auto f) {
+ return [&, width, f] {
+ size_t pre = buffer.size();
+ f();
+ if (size_t wrote = buffer.size() - pre; wrote < width)
+ tracker.padDebt = width - wrote;
+ };
+ };
+
+ auto strFn = [&](StringData s) { return [&, s] { write(s); }; };
+ auto escFn = [&](StringData s) { return [&, s] { str::escapeForJSON(buffer, s); }; };
+ auto intFn = [&](auto x) {
+ return [&, x] {
+ fmt::format_int s{x};
+ write(StringData{s.data(), s.size()});
+ };
+ };
+
+ auto quote = [&](auto f) {
+ return [&, f] {
+ write("\"");
+ f();
+ write("\"");
+ };
+ };
+
+ auto field = [&](CommaTracker& tracker, StringData name, auto f) {
+ writeComma(tracker);
+ quote(strFn(name))();
+ write(":");
+ f();
+ };
+
+ auto jsobj = [&](auto f) {
+ return [&, f] {
+ CommaTracker top;
+ write("{");
+ f(top);
+ write("}");
+ };
+ };
- buffer.push_back('}');
+ auto dateFn = [&](Date_t date) {
+ return jsobj([&, date](CommaTracker& tracker) {
+ field(tracker, "$date", quote([&, date] {
+ write(StringData{DateStringBuffer{}.iso8601(date, local)});
+ }));
+ });
+ };
+ auto bsonObjFn = [&](BSONObj o) {
+ return [&, o] { o.jsonStringBuffer(kFmt, 0, false, buffer, 0); };
+ };
+ auto bsonArrFn = [&](BSONArray a) {
+ return [&, a] { a.jsonStringBuffer(kFmt, 0, true, buffer, 0); };
+ };
+
+ jsobj([&](CommaTracker& top) {
+ field(top, c::kTimestampFieldName, dateFn(date));
+ field(top, c::kSeverityFieldName, padNextComma(top, 5, quote(strFn(severityString))));
+ field(top, c::kComponentFieldName, padNextComma(top, 11, quote(strFn(componentString))));
+ field(top, c::kIdFieldName, padNextComma(top, 8, intFn(id)));
+ field(top, c::kContextFieldName, quote(strFn(context)));
+ field(top, c::kMessageFieldName, quote(escFn(message)));
+ if (!attrs.empty()) {
+ JSONValueExtractor extractor(buffer, attributeMaxSize);
+ field(top, c::kAttributesFieldName, jsobj([&](CommaTracker&) {
+ attrs.apply(extractor);
+ }));
+ if (BSONObj o = extractor.truncated(); !o.isEmpty())
+ field(top, c::kTruncatedFieldName, bsonObjFn(o));
+ if (BSONObj o = extractor.truncatedSizes(); !o.isEmpty())
+ field(top, c::kTruncatedSizeFieldName, bsonObjFn(o));
+ }
+ if (tags != LogTag::kNone)
+ field(top, c::kTagsFieldName, bsonArrFn(tags.toBSONArray()));
+ })();
}
void JSONFormatter::operator()(boost::log::record_view const& rec,
diff --git a/src/mongo/logv2/text_formatter.cpp b/src/mongo/logv2/text_formatter.cpp
index 8314160d436..b0421abf8e9 100644
--- a/src/mongo/logv2/text_formatter.cpp
+++ b/src/mongo/logv2/text_formatter.cpp
@@ -44,20 +44,14 @@ namespace mongo::logv2 {
void TextFormatter::operator()(boost::log::record_view const& rec,
boost::log::formatting_ostream& strm) const {
- using namespace boost::log;
+ using boost::log::extract;
- Date_t timeStamp = extract<Date_t>(attributes::timeStamp(), rec).get();
fmt::memory_buffer buffer;
- switch (_timestampFormat) {
- case LogTimestampFormat::kISO8601UTC:
- outputDateAsISOStringUTC(buffer, timeStamp);
- break;
- case LogTimestampFormat::kISO8601Local:
- outputDateAsISOStringLocal(buffer, timeStamp);
- break;
- };
fmt::format_to(buffer,
- " {:<2} {:<8} [{}] ",
+ "{} {:<2} {:<8} [{}] ",
+ StringData{DateStringBuffer{}.iso8601(
+ extract<Date_t>(attributes::timeStamp(), rec).get(),
+ _timestampFormat == LogTimestampFormat::kISO8601Local)},
extract<LogSeverity>(attributes::severity(), rec).get().toStringDataCompact(),
extract<LogComponent>(attributes::component(), rec).get().getNameForLog(),
extract<StringData>(attributes::threadName(), rec).get());
diff --git a/src/mongo/util/time_support.cpp b/src/mongo/util/time_support.cpp
index 2d445650f20..4de71bcc251 100644
--- a/src/mongo/util/time_support.cpp
+++ b/src/mongo/util/time_support.cpp
@@ -115,8 +115,6 @@ bool Date_t::isFormattable() const {
long long jsTime_virtual_skew = 0;
thread_local long long jsTime_virtual_thread_skew = 0;
-using std::string;
-
void time_t_to_Struct(time_t t, struct tm* buf, bool local) {
#if defined(_WIN32)
if (local)
@@ -147,7 +145,7 @@ std::string time_t_to_String_short(time_t t) {
// uses ISO 8601 dates without trailing Z
// colonsOk should be false when creating filenames
-string terseCurrentTime(bool colonsOk) {
+std::string terseCurrentTime(bool colonsOk) {
struct tm t;
time_t_to_Struct(time(nullptr), &t);
@@ -157,39 +155,37 @@ string terseCurrentTime(bool colonsOk) {
return buf;
}
-string terseUTCCurrentTime() {
+std::string terseUTCCurrentTime() {
return terseCurrentTime(false) + "Z";
}
-#define MONGO_ISO_DATE_FMT_NO_TZ "%Y-%m-%dT%H:%M:%S"
-
-namespace {
-struct DateStringBuffer {
- static const int dataCapacity = 64;
- char data[dataCapacity];
- int size;
-};
-
-void _dateToISOString(Date_t date, bool local, DateStringBuffer* result) {
+DateStringBuffer& DateStringBuffer::iso8601(Date_t date, bool local) {
invariant(date.isFormattable());
- static const int bufSize = DateStringBuffer::dataCapacity;
- char* const buf = result->data;
+
struct tm t;
time_t_to_Struct(date.toTimeT(), &t, local);
- int pos = strftime(buf, bufSize, MONGO_ISO_DATE_FMT_NO_TZ, &t);
- dassert(0 < pos);
- char* cur = buf + pos;
- int bufRemaining = bufSize - pos;
- static const auto fmt_str_millis = fmt::compile<int32_t>(".{:03}");
- pos = fmt::format_to_n(
- cur, bufRemaining, fmt_str_millis, static_cast<int32_t>(date.asInt64() % 1000))
- .size;
- dassert(bufRemaining > pos && pos > 0);
- cur += pos;
- bufRemaining -= pos;
+
+ char* cur = _data.data();
+ char* end = _data.data() + _data.size();
+
+ {
+ static constexpr char kIsoDateFmtNoTz[] = "%Y-%m-%dT%H:%M:%S";
+ size_t n = strftime(cur, end - cur, kIsoDateFmtNoTz, &t);
+ dassert(n > 0);
+ cur += n;
+ }
+
+ {
+ static const auto& fmt_str_millis = *new auto(fmt::compile<int32_t>(".{:03}"));
+ auto res = fmt::format_to_n(
+ cur, end - cur, fmt_str_millis, static_cast<int32_t>(date.asInt64() % 1000));
+ cur = res.out;
+ dassert(cur < end && res.size > 0);
+ }
+
if (local) {
- static const int localTzSubstrLen = 6;
- dassert(bufRemaining >= localTzSubstrLen + 1);
+ static const size_t localTzSubstrLen = 6;
+ dassert(static_cast<size_t>(end - cur) >= localTzSubstrLen + 1);
#ifdef _WIN32
// NOTE(schwerin): The value stored by _get_timezone is the value one adds to local time
// to get UTC. This is opposite of the ISO-8601 meaning of the timezone offset.
@@ -204,47 +200,59 @@ void _dateToISOString(Date_t date, bool local, DateStringBuffer* result) {
const long tzOffsetSeconds = msTimeZone * (tzIsWestOfUTC ? 1 : -1);
const long tzOffsetHoursPart = tzOffsetSeconds / 3600;
const long tzOffsetMinutesPart = (tzOffsetSeconds / 60) % 60;
+
+ // "+hh:mm"
static const auto& fmtStrTime = *new auto(fmt::compile<char, long, long>("{}{:02}:{:02}"));
- fmt::format_to_n(cur,
- localTzSubstrLen + 1,
- fmtStrTime,
- tzIsWestOfUTC ? '-' : '+',
- tzOffsetHoursPart,
- tzOffsetMinutesPart);
+ cur = fmt::format_to_n(cur,
+ localTzSubstrLen + 1,
+ fmtStrTime,
+ tzIsWestOfUTC ? '-' : '+',
+ tzOffsetHoursPart,
+ tzOffsetMinutesPart)
+ .out;
#else
// ISO 8601 requires the timezone to be in hh:mm format which strftime can't produce
// See https://tools.ietf.org/html/rfc3339#section-5.6
- strftime(cur, bufRemaining, "%z:", &t);
+ strftime(cur, end - cur, "%z:", &t);
// cur will be written as +hhmm:, transform to +hh:mm
std::rotate(cur + 3, cur + 5, cur + 6);
+ cur += 6;
#endif
- cur += localTzSubstrLen;
} else {
- dassert(bufRemaining >= 2);
- *cur = 'Z';
- ++cur;
+ dassert(cur + 2 <= end);
+ *cur++ = 'Z';
}
- result->size = cur - buf;
- dassert(result->size < DateStringBuffer::dataCapacity);
+
+ dassert(cur <= end);
+ _size = cur - _data.data();
+ return *this;
}
-void _dateToCtimeString(Date_t date, DateStringBuffer* result) {
- static const size_t ctimeSubstrLen = 19;
- static const size_t millisSubstrLen = 4;
+DateStringBuffer& DateStringBuffer::ctime(Date_t date) {
+ // "Wed Jun 30 21:49:08 1993\n" // full asctime/ctime format
+ // "Wed Jun 30 21:49:08" // clip after position 19.
+ // "Wed Jun 30 21:49:08.996" // append millis
+ // 12345678901234567890123456
time_t t = date.toTimeT();
#if defined(_WIN32)
- ctime_s(result->data, sizeof(result->data), &t);
+ ctime_s(_data.data(), _data.size(), &t);
#else
- ctime_r(&t, result->data);
+ ctime_r(&t, _data.data());
#endif
- char* milliSecStr = result->data + ctimeSubstrLen;
+
+ static constexpr size_t ctimeSubstrLen = 19;
+ static constexpr size_t millisSubstrLen = 4;
+ char* milliSecStr = _data.data() + ctimeSubstrLen;
snprintf(milliSecStr,
millisSubstrLen + 1,
".%03u",
static_cast<unsigned>(date.toMillisSinceEpoch() % 1000));
- result->size = ctimeSubstrLen + millisSubstrLen;
+ _size = ctimeSubstrLen + millisSubstrLen;
+ return *this;
}
+namespace {
+
#if defined(_WIN32)
uint64_t fileTimeToMicroseconds(FILETIME const ft) {
@@ -267,57 +275,6 @@ uint64_t fileTimeToMicroseconds(FILETIME const ft) {
#endif
-} // namespace
-
-std::string dateToISOStringUTC(Date_t date) {
- DateStringBuffer buf;
- _dateToISOString(date, false, &buf);
- return std::string(buf.data, buf.size);
-}
-
-std::string dateToISOStringLocal(Date_t date) {
- DateStringBuffer buf;
- _dateToISOString(date, true, &buf);
- return std::string(buf.data, buf.size);
-}
-
-std::string dateToCtimeString(Date_t date) {
- DateStringBuffer buf;
- _dateToCtimeString(date, &buf);
- return std::string(buf.data, buf.size);
-}
-
-void outputDateAsISOStringUTC(std::ostream& os, Date_t date) {
- DateStringBuffer buf;
- _dateToISOString(date, false, &buf);
- os << StringData(buf.data, buf.size);
-}
-
-void outputDateAsISOStringUTC(fmt::memory_buffer& buffer, Date_t date) {
- DateStringBuffer buf;
- _dateToISOString(date, false, &buf);
- buffer.append(buf.data, buf.data + buf.size);
-}
-
-void outputDateAsISOStringLocal(std::ostream& os, Date_t date) {
- DateStringBuffer buf;
- _dateToISOString(date, true, &buf);
- os << StringData(buf.data, buf.size);
-}
-
-void outputDateAsISOStringLocal(fmt::memory_buffer& buffer, Date_t date) {
- DateStringBuffer buf;
- _dateToISOString(date, true, &buf);
- buffer.append(buf.data, buf.data + buf.size);
-}
-
-void outputDateAsCtime(std::ostream& os, Date_t date) {
- DateStringBuffer buf;
- _dateToCtimeString(date, &buf);
- os << StringData(buf.data, buf.size);
-}
-
-namespace {
StringData getNextToken(StringData currentString,
StringData terminalChars,
size_t startIndex,
@@ -753,8 +710,6 @@ StatusWith<Date_t> dateFromISOString(StringData dateString) {
return Date_t::fromMillisSinceEpoch(static_cast<long long>(resultMillis));
}
-#undef MONGO_ISO_DATE_FMT_NO_TZ
-
std::string Date_t::toString() const {
if (isFormattable()) {
return dateToISOStringLocal(*this);
@@ -925,5 +880,24 @@ Nanoseconds getMinimumTimerResolution() {
return minTimerResolution;
}
+std::string dateToISOStringUTC(Date_t date) {
+ return std::string{DateStringBuffer{}.iso8601(date, false)};
+}
+std::string dateToISOStringLocal(Date_t date) {
+ return std::string{DateStringBuffer{}.iso8601(date, true)};
+}
+std::string dateToCtimeString(Date_t date) {
+ return std::string{DateStringBuffer{}.ctime(date)};
+}
+
+void outputDateAsISOStringUTC(std::ostream& os, Date_t date) {
+ os << StringData{DateStringBuffer{}.iso8601(date, false)};
+}
+void outputDateAsISOStringLocal(std::ostream& os, Date_t date) {
+ os << StringData{DateStringBuffer{}.iso8601(date, true)};
+}
+void outputDateAsCtime(std::ostream& os, Date_t date) {
+ os << StringData{DateStringBuffer{}.ctime(date)};
+}
} // namespace mongo
diff --git a/src/mongo/util/time_support.h b/src/mongo/util/time_support.h
index 3072e62302a..f7ba73ca26d 100644
--- a/src/mongo/util/time_support.h
+++ b/src/mongo/util/time_support.h
@@ -29,8 +29,8 @@
#pragma once
+#include <array>
#include <ctime>
-#include <fmt/format.h>
#include <iosfwd>
#include <limits>
#include <string>
@@ -263,8 +263,34 @@ private:
static AtomicWord<long long> lastNowVal;
};
-// uses ISO 8601 dates without trailing Z
-// colonsOk should be false when creating filenames
+class DateStringBuffer {
+public:
+ /** Fill with formatted `date`, either in `local` or UTC. */
+ DateStringBuffer& iso8601(Date_t date, bool local);
+
+ /**
+ * Fill with formatted `date`, in modified ctime format.
+ * Like ctime, but newline and year removed, and milliseconds added.
+ */
+ DateStringBuffer& ctime(Date_t date);
+
+ explicit operator StringData() const {
+ return StringData{_data.data(), _size};
+ }
+
+ explicit operator std::string() const {
+ return std::string{StringData{*this}};
+ }
+
+private:
+ std::array<char, 64> _data;
+ size_t _size = 0;
+};
+
+/**
+ * uses ISO 8601 dates without trailing "Z".
+ * `colonsOk` should be false when creating filenames.
+ */
std::string terseCurrentTime(bool colonsOk = true);
/**
@@ -272,28 +298,25 @@ std::string terseCurrentTime(bool colonsOk = true);
*/
std::string terseUTCCurrentTime();
+/** @{ */
/**
- * Formats "date" according to the ISO 8601 extended form standard, including date,
- * and time with milliseconds decimal component, in the UTC timezone.
- *
- * Sample format: "2013-07-23T18:42:14.072Z"
+ * Formats "date" in 3 formats to 3 kinds of output.
+ * Function variants are provided to produce ISO local, ISO UTC, or modified ctime formats.
+ * The ISO formats are according to the ISO 8601 extended form standard, including date and
+ * time with a milliseconds decimal component.
+ * Modified ctime format is like `ctime`, but with milliseconds and no year.
+ * "2013-07-23T18:42:14.072Z" // *ToISOStringUTC
+ * "2013-07-23T18:42:14.072-05:00" // *ToISOStringLocal
+ * "Wed Oct 31 13:34:47.996" // *ToCtimeString (modified ctime)
+ * Output can be a std::string, or put to a std::ostream.
*/
std::string dateToISOStringUTC(Date_t date);
-
-/**
- * Formats "date" according to the ISO 8601 extended form standard, including date,
- * and time with milliseconds decimal component, in the local timezone.
- *
- * Sample format: "2013-07-23T18:42:14.072-05:00"
- */
std::string dateToISOStringLocal(Date_t date);
-
-/**
- * Formats "date" in fixed width in the local time zone.
- *
- * Sample format: "Wed Oct 31 13:34:47.996"
- */
std::string dateToCtimeString(Date_t date);
+void outputDateAsISOStringUTC(std::ostream& os, Date_t date);
+void outputDateAsISOStringLocal(std::ostream& os, Date_t date);
+void outputDateAsCtime(std::ostream& os, Date_t date);
+/** @} */
/**
* Parses a Date_t from an ISO 8601 std::string representation.
@@ -305,23 +328,6 @@ std::string dateToCtimeString(Date_t date);
*/
StatusWith<Date_t> dateFromISOString(StringData dateString);
-/**
- * Like dateToISOStringUTC, except outputs to a std::ostream or fmt::memory_buffer.
- */
-void outputDateAsISOStringUTC(std::ostream& os, Date_t date);
-void outputDateAsISOStringUTC(fmt::memory_buffer& buffer, Date_t date);
-
-/**
- * Like dateToISOStringLocal, except outputs to a std::ostream or fmt::memory_buffer.
- */
-void outputDateAsISOStringLocal(std::ostream& os, Date_t date);
-void outputDateAsISOStringLocal(fmt::memory_buffer& buffer, Date_t date);
-
-/**
- * Like dateToCtimeString, except outputs to a std::ostream.
- */
-void outputDateAsCtime(std::ostream& os, Date_t date);
-
void sleepsecs(int s);
void sleepmillis(long long ms);
void sleepmicros(long long micros);