summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaun Verch <shaun.verch@mongodb.com>2014-05-13 17:01:04 -0400
committerShaun Verch <shaun.verch@mongodb.com>2014-05-30 14:49:02 -0400
commitad9df9f23b3ff4c831ff642817bd2befb8bf4609 (patch)
tree6f05d60ec0fd9c3a10e979277867e9a1f92750cb
parentc73f7c12cde09575b11db1dba91d852c724d076c (diff)
downloadmongo-ad9df9f23b3ff4c831ff642817bd2befb8bf4609.tar.gz
SERVER-13760 Do not call dateToISOString if date is not formatable
(cherry picked from commit 99fa4e6058a24d6c4d7fde19ce940719c5bbc210)
-rw-r--r--jstests/tool/exportimport_date.js46
-rw-r--r--src/mongo/db/jsobj.cpp26
-rw-r--r--src/mongo/dbtests/jsontests.cpp8
-rw-r--r--src/mongo/tools/export.cpp5
-rw-r--r--src/mongo/util/time_support.cpp10
-rw-r--r--src/mongo/util/time_support.h1
-rw-r--r--src/mongo/util/time_support_test.cpp13
7 files changed, 101 insertions, 8 deletions
diff --git a/jstests/tool/exportimport_date.js b/jstests/tool/exportimport_date.js
new file mode 100644
index 00000000000..81476471c2f
--- /dev/null
+++ b/jstests/tool/exportimport_date.js
@@ -0,0 +1,46 @@
+var tt = new ToolTest('exportimport_date_test');
+
+var exportimport_db = tt.startDB();
+
+var src = exportimport_db.src;
+var dst = exportimport_db.dst;
+
+src.drop();
+dst.drop();
+
+// Insert a date that we can format
+var formatable = ISODate("1970-01-01T05:00:00Z");
+assert.eq(formatable.valueOf(), 18000000);
+src.insert({ "_id" : formatable });
+
+// Insert a date that we cannot format as an ISODate string
+var nonformatable = ISODate("3001-01-01T00:00:00Z");
+assert.eq(nonformatable.valueOf(), 32535216000000);
+src.insert({ "_id" : nonformatable });
+
+data = 'data/exportimport_date_test.json';
+
+print('About to call mongoexport on: ' + exportimport_db.getName() + '.' + src.getName() +
+ ' with file: ' + data);
+tt.runTool('export', '--out' , data, '-d', exportimport_db.getName(), '-c', src.getName());
+
+print('About to call mongoimport on: ' + exportimport_db.getName() + '.' + dst.getName() +
+ ' with file: ' + data);
+tt.runTool('import', '--file', data, '-d', exportimport_db.getName(), '-c', dst.getName());
+
+print('About to verify that source and destination collections match');
+
+src_cursor = src.find().sort({ _id : 1 });
+dst_cursor = dst.find().sort({ _id : 1 });
+
+var documentCount = 0;
+while (src_cursor.hasNext()) {
+ assert(dst_cursor.hasNext(), 'Source has more documents than destination. ' +
+ 'Destination has ' + documentCount + ' documents.');
+ assert.eq(src_cursor.next(), dst_cursor.next(), 'Mismatch on document ' + documentCount);
+ ++documentCount;
+}
+assert(!dst_cursor.hasNext(), 'Destination has more documents than source. ' +
+ 'Source has ' + documentCount + ' documents.');
+
+print('Verified that source and destination collections match');
diff --git a/src/mongo/db/jsobj.cpp b/src/mongo/db/jsobj.cpp
index 02a8ebd6549..ea5e1e48fb0 100644
--- a/src/mongo/db/jsobj.cpp
+++ b/src/mongo/db/jsobj.cpp
@@ -211,11 +211,17 @@ namespace mongo {
if (format == Strict) {
Date_t d = date();
s << "{ \"$date\" : ";
- if (static_cast<long long>(d.millis) < 0) {
- s << "{ \"$numberLong\" : \"" << static_cast<long long>(d.millis) << "\" }";
+ // The two cases in which we cannot convert Date_t::millis to an ISO Date string are
+ // when the date is too large to format (SERVER-13760), and when the date is before
+ // the epoch (SERVER-11273). Since Date_t internally stores millis as an unsigned
+ // long long, despite the fact that it is logically signed (SERVER-8573), this check
+ // handles both the case where Date_t::millis is too large, and the case where
+ // Date_t::millis is negative (before the epoch).
+ if (d.isFormatable()) {
+ s << "\"" << dateToISOStringLocal(date()) << "\"";
}
else {
- s << "\"" << dateToISOStringLocal(date()) << "\"";
+ s << "{ \"$numberLong\" : \"" << static_cast<long long>(d.millis) << "\" }";
}
s << " }";
}
@@ -223,14 +229,20 @@ namespace mongo {
s << "Date( ";
if (pretty) {
Date_t d = date();
- if (static_cast<long long>(d.millis) < 0) {
+ // The two cases in which we cannot convert Date_t::millis to an ISO Date string
+ // are when the date is too large to format (SERVER-13760), and when the date is
+ // before the epoch (SERVER-11273). Since Date_t internally stores millis as an
+ // unsigned long long, despite the fact that it is logically signed
+ // (SERVER-8573), this check handles both the case where Date_t::millis is too
+ // large, and the case where Date_t::millis is negative (before the epoch).
+ if (d.isFormatable()) {
+ s << "\"" << dateToISOStringLocal(date()) << "\"";
+ }
+ else {
// FIXME: This is not parseable by the shell, since it may not fit in a
// float
s << d.millis;
}
- else {
- s << "\"" << dateToISOStringLocal(date()) << "\"";
- }
}
else {
s << date().asInt64();
diff --git a/src/mongo/dbtests/jsontests.cpp b/src/mongo/dbtests/jsontests.cpp
index c5d63739207..a8e17ca4ba5 100644
--- a/src/mongo/dbtests/jsontests.cpp
+++ b/src/mongo/dbtests/jsontests.cpp
@@ -418,6 +418,14 @@ namespace JsonTests {
built.jsonString( Strict ) );
ASSERT_EQUALS( "{ \"a\" : Date( 0 ) }", built.jsonString( TenGen ) );
ASSERT_EQUALS( "{ \"a\" : Date( 0 ) }", built.jsonString( JS ) );
+
+ // Test dates above our maximum formattable date. See SERVER-13760.
+ BSONObjBuilder b2;
+ b2.appendDate("a", 32535262800000ULL);
+ BSONObj built2 = b2.done();
+ ASSERT_EQUALS(
+ "{ \"a\" : { \"$date\" : { \"$numberLong\" : \"32535262800000\" } } }",
+ built2.jsonString( Strict ) );
}
private:
diff --git a/src/mongo/tools/export.cpp b/src/mongo/tools/export.cpp
index 5e9bca22432..75460f76a15 100644
--- a/src/mongo/tools/export.cpp
+++ b/src/mongo/tools/export.cpp
@@ -92,7 +92,10 @@ public:
case jstOID:
return "ObjectID(" + object.OID().toString() + ")"; // OIDs are always 24 bytes
case Date:
- return dateToISOStringUTC(object.Date());
+ // We need to check if we can actually format this date. See SERVER-13760.
+ return object.Date().isFormatable() ?
+ dateToISOStringUTC(object.Date()) :
+ csvEscape(object.jsonString(Strict, false));
case Timestamp:
return csvEscape(object.jsonString(Strict, false));
case RegEx:
diff --git a/src/mongo/util/time_support.cpp b/src/mongo/util/time_support.cpp
index 146d5ad02a4..8de951a9608 100644
--- a/src/mongo/util/time_support.cpp
+++ b/src/mongo/util/time_support.cpp
@@ -49,6 +49,15 @@ timegm(struct tm *const tmp);
namespace mongo {
+ bool Date_t::isFormatable() const {
+ if (sizeof(time_t) == sizeof(int32_t)) {
+ return millis < 2147483647000ULL; // "2038-01-19T03:14:07Z"
+ }
+ else {
+ return millis < 32535215999000ULL; // "3000-12-31T23:59:59Z"
+ }
+ }
+
// jsTime_virtual_skew is just for testing. a test command manipulates it.
long long jsTime_virtual_skew = 0;
boost::thread_specific_ptr<long long> jsTime_virtual_thread_skew;
@@ -118,6 +127,7 @@ namespace mongo {
}
static inline std::string _dateToISOString(Date_t date, bool local) {
+ invariant(date.isFormatable());
const int bufSize = 32;
char buf[bufSize];
struct tm t;
diff --git a/src/mongo/util/time_support.h b/src/mongo/util/time_support.h
index 32f6522152c..8073229dc52 100644
--- a/src/mongo/util/time_support.h
+++ b/src/mongo/util/time_support.h
@@ -44,6 +44,7 @@ namespace mongo {
int64_t asInt64() const {
return static_cast<int64_t>(millis);
}
+ bool isFormatable() const;
};
// uses ISO 8601 dates without trailing Z
diff --git a/src/mongo/util/time_support_test.cpp b/src/mongo/util/time_support_test.cpp
index 318125c7fdb..d6e318bddec 100644
--- a/src/mongo/util/time_support_test.cpp
+++ b/src/mongo/util/time_support_test.cpp
@@ -126,6 +126,10 @@ namespace {
swull = dateFromISOString("2058-02-20T18:29:11.100Z");
ASSERT_OK(swull.getStatus());
ASSERT_EQUALS(swull.getValue(), 2781455351100ULL);
+
+ swull = dateFromISOString("3001-01-01T08:00:00.000Z");
+ ASSERT_OK(swull.getStatus());
+ ASSERT_EQUALS(swull.getValue(), 32535244800000ULL);
}
swull = dateFromISOString("2013-02-20T18:29:11.100Z");
@@ -196,6 +200,15 @@ namespace {
swull = dateFromISOString("2058-02-20T13:29:11.100-0500");
ASSERT_OK(swull.getStatus());
ASSERT_EQUALS(swull.getValue(), 2781455351100ULL);
+
+ swull = dateFromISOString("3000-12-31T23:59:59Z");
+ ASSERT_OK(swull.getStatus());
+ ASSERT_EQUALS(swull.getValue(), 32535215999000ULL);
+ }
+ else {
+ swull = dateFromISOString("2038-01-19T03:14:07Z");
+ ASSERT_OK(swull.getStatus());
+ ASSERT_EQUALS(swull.getValue(), 2147483647000ULL);
}
swull = dateFromISOString("2013-02-20T13:29:11.100-0500");