Skip to content

Commit

Permalink
SERVER-13760 Do not call dateToISOString if date is not formatable
Browse files Browse the repository at this point in the history
  • Loading branch information
sverch committed May 23, 2014
1 parent ed71c30 commit 99fa4e6
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 8 deletions.
46 changes: 46 additions & 0 deletions jstests/tool/exportimport_date.js
Original file line number Diff line number Diff line change
@@ -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');
26 changes: 19 additions & 7 deletions src/mongo/db/jsobj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,26 +223,38 @@ 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 << " }";
}
else {
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();
Expand Down
8 changes: 8 additions & 0 deletions src/mongo/dbtests/jsontests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
5 changes: 4 additions & 1 deletion src/mongo/tools/export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,10 @@ class Export : public Tool {
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:
Expand Down
10 changes: 10 additions & 0 deletions src/mongo/util/time_support.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,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;
Expand Down Expand Up @@ -137,6 +146,7 @@ namespace {
};

void _dateToISOString(Date_t date, bool local, DateStringBuffer* result) {
invariant(date.isFormatable());
static const int bufSize = DateStringBuffer::dataCapacity;
char* const buf = result->data;
struct tm t;
Expand Down
1 change: 1 addition & 0 deletions src/mongo/util/time_support.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ namespace mongo {
int64_t asInt64() const {
return static_cast<int64_t>(millis);
}
bool isFormatable() const;
};

// uses ISO 8601 dates without trailing Z
Expand Down
13 changes: 13 additions & 0 deletions src/mongo/util/time_support_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,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");
Expand Down Expand Up @@ -250,6 +254,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");
Expand Down

0 comments on commit 99fa4e6

Please sign in to comment.