summaryrefslogtreecommitdiff
path: root/src/mongo/util/time_support.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/util/time_support.cpp')
-rw-r--r--src/mongo/util/time_support.cpp610
1 files changed, 590 insertions, 20 deletions
diff --git a/src/mongo/util/time_support.cpp b/src/mongo/util/time_support.cpp
index 901cedbe6f8..8de951a9608 100644
--- a/src/mongo/util/time_support.cpp
+++ b/src/mongo/util/time_support.cpp
@@ -14,7 +14,7 @@
*/
#include "mongo/platform/basic.h"
-#include "mongo/platform/cstdint.h"
+
#include "mongo/util/time_support.h"
#include <cstdio>
@@ -24,16 +24,40 @@
#include <boost/thread/tss.hpp>
#include <boost/thread/xtime.hpp>
+#include "mongo/base/parse_number.h"
+#include "mongo/bson/util/builder.h"
+#include "mongo/platform/cstdint.h"
#include "mongo/util/assert_util.h"
#ifdef _WIN32
#include <boost/date_time/filetime_functions.hpp>
#include "mongo/util/concurrency/mutex.h"
#include "mongo/util/timer.h"
+
+// NOTE(schwerin): MSVC's _snprintf is not a drop-in replacement for C99's snprintf(). In
+// particular, when the target buffer is too small, behaviors differ. Consult the documentation
+// from MSDN and form the BSD or Linux man pages before using.
+#define snprintf _snprintf
+#endif
+
+#ifdef __sunos__
+// Some versions of Solaris do not have timegm defined, so fall back to our implementation when
+// building on Solaris. See SERVER-13446.
+extern "C" time_t
+timegm(struct tm *const tmp);
#endif
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;
@@ -54,25 +78,30 @@ namespace mongo {
#endif
}
+ std::string time_t_to_String(time_t t) {
+ char buf[64];
#if defined(_WIN32)
- void curTimeString(char* timeStr) {
- boost::xtime xt;
- boost::xtime_get(&xt, MONGO_BOOST_TIME_UTC);
- time_t_to_String(xt.sec, timeStr);
-
- char* milliSecStr = timeStr + 19;
- _snprintf(milliSecStr, 5, ".%03d", static_cast<int32_t>(xt.nsec / 1000000));
- }
+ ctime_s(buf, sizeof(buf), &t);
#else
- void curTimeString(char* timeStr) {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- time_t_to_String(tv.tv_sec, timeStr);
-
- char* milliSecStr = timeStr + 19;
- snprintf(milliSecStr, 5, ".%03d", static_cast<int32_t>(tv.tv_usec / 1000));
+ ctime_r(&t, buf);
+#endif
+ buf[24] = 0; // don't want the \n
+ return buf;
}
+
+ std::string time_t_to_String_short(time_t t) {
+ char buf[64];
+#if defined(_WIN32)
+ ctime_s(buf, sizeof(buf), &t);
+#else
+ ctime_r(&t, buf);
#endif
+ buf[19] = 0;
+ if( buf[0] && buf[1] && buf[2] && buf[3] )
+ return buf + 4; // skip day of week
+ return buf;
+ }
+
// uses ISO 8601 dates without trailing Z
// colonsOk should be false when creating filenames
@@ -86,16 +115,550 @@ namespace mongo {
return buf;
}
+#define MONGO_ISO_DATE_FMT_NO_TZ "%Y-%m-%dT%H:%M:%S"
string timeToISOString(time_t time) {
struct tm t;
time_t_to_Struct( time, &t );
- const char* fmt = "%Y-%m-%dT%H:%M:%SZ";
+ const char* fmt = MONGO_ISO_DATE_FMT_NO_TZ "Z";
char buf[32];
fassert(16227, strftime(buf, sizeof(buf), fmt, &t) == 20);
return buf;
}
+ static inline std::string _dateToISOString(Date_t date, bool local) {
+ invariant(date.isFormatable());
+ const int bufSize = 32;
+ char buf[bufSize];
+ struct tm t;
+ time_t_to_Struct(date.toTimeT(), &t, local);
+ int pos = strftime(buf, bufSize, MONGO_ISO_DATE_FMT_NO_TZ, &t);
+ fassert(16981, 0 < pos);
+ char* cur = buf + pos;
+ int bufRemaining = bufSize - pos;
+ pos = snprintf(cur, bufRemaining, ".%03d", static_cast<int32_t>(date.asInt64() % 1000));
+ fassert(16982, bufRemaining > pos && pos > 0);
+ cur += pos;
+ bufRemaining -= pos;
+ if (local) {
+ fassert(16983, bufRemaining >= 6);
+#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.
+ // NOTE(schwerin): Microsoft's timezone code always assumes US rules for daylight
+ // savings time. We can do no better without completely reimplementing localtime_s and
+ // related time library functions.
+ long msTimeZone;
+ _get_timezone(&msTimeZone);
+ if (t.tm_isdst) msTimeZone -= 3600;
+ const bool tzIsWestOfUTC = msTimeZone > 0;
+ const long tzOffsetSeconds = msTimeZone* (tzIsWestOfUTC ? 1 : -1);
+ const long tzOffsetHoursPart = tzOffsetSeconds / 3600;
+ const long tzOffsetMinutesPart = (tzOffsetSeconds / 60) % 60;
+ snprintf(cur, 6, "%c%02ld%02ld",
+ tzIsWestOfUTC ? '-' : '+',
+ tzOffsetHoursPart,
+ tzOffsetMinutesPart);
+#else
+ strftime(cur, bufRemaining, "%z", &t);
+#endif
+ }
+ else {
+ fassert(16984, bufRemaining >= 2);
+ *cur = 'Z';
+ ++cur;
+ *cur = '\0';
+ }
+ return buf;
+ }
+
+ std::string dateToISOStringUTC(Date_t date) {
+ return _dateToISOString(date, false);
+ }
+
+ std::string dateToISOStringLocal(Date_t date) {
+ return _dateToISOString(date, true);
+ }
+
+namespace {
+ StringData getNextToken(const StringData& currentString,
+ const StringData& terminalChars,
+ size_t startIndex,
+ size_t* endIndex) {
+ size_t index = startIndex;
+
+ if (index == std::string::npos) {
+ *endIndex = std::string::npos;
+ return StringData();
+ }
+
+ for (; index < currentString.size(); index++) {
+ if (terminalChars.find(currentString[index]) != std::string::npos) {
+ break;
+ }
+ }
+
+ // substr just returns the rest of the string if the length passed in is greater than the
+ // number of characters remaining, and since std::string::npos is the length of the largest
+ // possible string we know (std::string::npos - startIndex) is at least as long as the rest
+ // of the string. That means this handles both the case where we hit a terminating
+ // character and we want a substring, and the case where didn't and just want the rest of
+ // the string.
+ *endIndex = (index < currentString.size() ? index : std::string::npos);
+ return currentString.substr(startIndex, index - startIndex);
+ }
+
+ // Check to make sure that the string only consists of digits
+ bool isOnlyDigits(const StringData& toCheck) {
+ StringData digits("0123456789");
+ for (StringData::const_iterator iterator = toCheck.begin();
+ iterator != toCheck.end(); iterator++) {
+ if (digits.find(*iterator) == std::string::npos) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ Status parseTimeZoneFromToken(const StringData& tzStr, int* tzAdjSecs) {
+
+ *tzAdjSecs = 0;
+
+ if (!tzStr.empty()) {
+ if (tzStr[0] == 'Z') {
+ if (tzStr.size() != 1) {
+ StringBuilder sb;
+ sb << "Found trailing characters in time zone specifier: " << tzStr;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+ }
+ else if (tzStr[0] == '+' || tzStr[0] == '-') {
+ if (tzStr.size() != 5 || !isOnlyDigits(tzStr.substr(1, 4))) {
+ StringBuilder sb;
+ sb << "Time zone adjustment string should be four digits: " << tzStr;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ // Parse the hours component of the time zone offset. Note that
+ // parseNumberFromStringWithBase correctly handles the sign bit, so leave that in.
+ StringData tzHoursStr = tzStr.substr(0, 3);
+ int tzAdjHours = 0;
+ Status status = parseNumberFromStringWithBase(tzHoursStr, 10, &tzAdjHours);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (tzAdjHours < -23 || tzAdjHours > 23) {
+ StringBuilder sb;
+ sb << "Time zone hours adjustment out of range: " << tzAdjHours;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ StringData tzMinutesStr = tzStr.substr(3, 2);
+ int tzAdjMinutes = 0;
+ status = parseNumberFromStringWithBase(tzMinutesStr, 10, &tzAdjMinutes);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (tzAdjMinutes < 0 || tzAdjMinutes > 59) {
+ StringBuilder sb;
+ sb << "Time zone minutes adjustment out of range: " << tzAdjMinutes;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ // Use the sign that parseNumberFromStringWithBase found to determine if we need to
+ // flip the sign of our minutes component. Also, we need to flip the sign of our
+ // final result, because the offset passed in by the user represents how far off the
+ // time they are giving us is from UTC, which means that we have to go the opposite
+ // way to compensate and get the UTC time
+ *tzAdjSecs = (-1) * ((tzAdjHours < 0 ? -1 : 1) * (tzAdjMinutes * 60) +
+ (tzAdjHours * 60 * 60));
+
+ // Disallow adjustiment of 24 hours or more in either direction (should be checked
+ // above as the separate components of minutes and hours)
+ fassert(17318, *tzAdjSecs > -86400 && *tzAdjSecs < 86400);
+ }
+ else {
+ StringBuilder sb;
+ sb << "Invalid time zone string: \"" << tzStr
+ << "\". Found invalid character at the beginning of time "
+ << "zone specifier: " << tzStr[0];
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+ }
+ else {
+ return Status(ErrorCodes::BadValue, "Missing required time zone specifier for date");
+ }
+
+ return Status::OK();
+ }
+
+ Status parseMillisFromToken(
+ const StringData& millisStr,
+ int* resultMillis) {
+
+ *resultMillis = 0;
+
+ if (!millisStr.empty()) {
+ if (millisStr.size() > 3 || !isOnlyDigits(millisStr)) {
+ StringBuilder sb;
+ sb << "Millisecond string should be at most three digits: " << millisStr;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ Status status = parseNumberFromStringWithBase(millisStr, 10, resultMillis);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ // Treat the digits differently depending on how many there are. 1 digit = hundreds of
+ // milliseconds, 2 digits = tens of milliseconds, 3 digits = milliseconds.
+ int millisMagnitude = 1;
+ if (millisStr.size() == 2) {
+ millisMagnitude = 10;
+ }
+ else if (millisStr.size() == 1) {
+ millisMagnitude = 100;
+ }
+
+ *resultMillis = *resultMillis * millisMagnitude;
+
+ if (*resultMillis < 0 || *resultMillis > 1000) {
+ StringBuilder sb;
+ sb << "Millisecond out of range: " << *resultMillis;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+ }
+
+ return Status::OK();
+ }
+
+ Status parseTmFromTokens(
+ const StringData& yearStr,
+ const StringData& monthStr,
+ const StringData& dayStr,
+ const StringData& hourStr,
+ const StringData& minStr,
+ const StringData& secStr,
+ std::tm* resultTm) {
+
+ memset(resultTm, 0, sizeof(*resultTm));
+
+ // Parse year
+ if (yearStr.size() != 4 || !isOnlyDigits(yearStr)) {
+ StringBuilder sb;
+ sb << "Year string should be four digits: " << yearStr;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ Status status = parseNumberFromStringWithBase(yearStr, 10, &resultTm->tm_year);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (resultTm->tm_year < 1970 || resultTm->tm_year > 9999) {
+ StringBuilder sb;
+ sb << "Year out of range: " << resultTm->tm_year;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ resultTm->tm_year -= 1900;
+
+ // Parse month
+ if (monthStr.size() != 2 || !isOnlyDigits(monthStr)) {
+ StringBuilder sb;
+ sb << "Month string should be two digits: " << monthStr;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ status = parseNumberFromStringWithBase(monthStr, 10, &resultTm->tm_mon);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (resultTm->tm_mon < 1 || resultTm->tm_mon > 12) {
+ StringBuilder sb;
+ sb << "Month out of range: " << resultTm->tm_mon;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ resultTm->tm_mon -= 1;
+
+ // Parse day
+ if (dayStr.size() != 2 || !isOnlyDigits(dayStr)) {
+ StringBuilder sb;
+ sb << "Day string should be two digits: " << dayStr;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ status = parseNumberFromStringWithBase(dayStr, 10, &resultTm->tm_mday);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (resultTm->tm_mday < 1 || resultTm->tm_mday > 31) {
+ StringBuilder sb;
+ sb << "Day out of range: " << resultTm->tm_mday;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ // Parse hour
+ if (hourStr.size() != 2 || !isOnlyDigits(hourStr)) {
+ StringBuilder sb;
+ sb << "Hour string should be two digits: " << hourStr;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ status = parseNumberFromStringWithBase(hourStr, 10, &resultTm->tm_hour);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (resultTm->tm_hour < 0 || resultTm->tm_hour > 23) {
+ StringBuilder sb;
+ sb << "Hour out of range: " << resultTm->tm_hour;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ // Parse minute
+ if (minStr.size() != 2 || !isOnlyDigits(minStr)) {
+ StringBuilder sb;
+ sb << "Minute string should be two digits: " << minStr;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ status = parseNumberFromStringWithBase(minStr, 10, &resultTm->tm_min);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (resultTm->tm_min < 0 || resultTm->tm_min > 59) {
+ StringBuilder sb;
+ sb << "Minute out of range: " << resultTm->tm_min;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ // Parse second if it exists
+ if (secStr.empty()) {
+ return Status::OK();
+ }
+
+ if (secStr.size() != 2 || !isOnlyDigits(secStr)) {
+ StringBuilder sb;
+ sb << "Second string should be two digits: " << secStr;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ status = parseNumberFromStringWithBase(secStr, 10, &resultTm->tm_sec);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (resultTm->tm_sec < 0 || resultTm->tm_sec > 59) {
+ StringBuilder sb;
+ sb << "Second out of range: " << resultTm->tm_sec;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ return Status::OK();
+ }
+
+ Status parseTm(const StringData& dateString,
+ std::tm* resultTm,
+ int* resultMillis,
+ int* tzAdjSecs) {
+ size_t yearEnd = std::string::npos;
+ size_t monthEnd = std::string::npos;
+ size_t dayEnd = std::string::npos;
+ size_t hourEnd = std::string::npos;
+ size_t minEnd = std::string::npos;
+ size_t secEnd = std::string::npos;
+ size_t millisEnd = std::string::npos;
+ size_t tzEnd = std::string::npos;
+ StringData yearStr, monthStr, dayStr, hourStr, minStr, secStr, millisStr, tzStr;
+
+ yearStr = getNextToken(dateString, "-", 0, &yearEnd);
+ monthStr = getNextToken(dateString, "-", yearEnd + 1, &monthEnd);
+ dayStr = getNextToken(dateString, "T", monthEnd + 1, &dayEnd);
+ hourStr = getNextToken(dateString, ":", dayEnd + 1, &hourEnd);
+ minStr = getNextToken(dateString, ":+-Z", hourEnd + 1, &minEnd);
+
+ // Only look for seconds if the character we matched for the end of the minutes token is a
+ // colon
+ if (minEnd != std::string::npos && dateString[minEnd] == ':') {
+ // Make sure the string doesn't end with ":"
+ if (minEnd == dateString.size() - 1) {
+ StringBuilder sb;
+ sb << "Invalid date: " << dateString << ". Ends with \"" << dateString[minEnd]
+ << "\" character";
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ secStr = getNextToken(dateString, ".+-Z", minEnd + 1, &secEnd);
+
+ // Make sure we actually got something for seconds, since here we know they are expected
+ if (secStr.empty()) {
+ StringBuilder sb;
+ sb << "Missing seconds in date: " << dateString;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+ }
+
+ // Only look for milliseconds if the character we matched for the end of the seconds token
+ // is a period
+ if (secEnd != std::string::npos && dateString[secEnd] == '.') {
+ // Make sure the string doesn't end with "."
+ if (secEnd == dateString.size() - 1) {
+ StringBuilder sb;
+ sb << "Invalid date: " << dateString << ". Ends with \"" << dateString[secEnd]
+ << "\" character";
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+
+ millisStr = getNextToken(dateString, "+-Z", secEnd + 1, &millisEnd);
+
+ // Make sure we actually got something for millis, since here we know they are expected
+ if (millisStr.empty()) {
+ StringBuilder sb;
+ sb << "Missing seconds in date: " << dateString;
+ return Status(ErrorCodes::BadValue, sb.str());
+ }
+ }
+
+ // Now look for the time zone specifier depending on which prefix of the time we provided
+ if (millisEnd != std::string::npos) {
+ tzStr = getNextToken(dateString, "", millisEnd, &tzEnd);
+ }
+ else if (secEnd != std::string::npos && dateString[secEnd] != '.') {
+ tzStr = getNextToken(dateString, "", secEnd, &tzEnd);
+ }
+ else if (minEnd != std::string::npos && dateString[minEnd] != ':') {
+ tzStr = getNextToken(dateString, "", minEnd, &tzEnd);
+ }
+
+ Status status = parseTmFromTokens(yearStr, monthStr, dayStr, hourStr, minStr, secStr,
+ resultTm);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ status = parseTimeZoneFromToken(tzStr, tzAdjSecs);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ status = parseMillisFromToken(millisStr, resultMillis);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return Status::OK();
+ }
+
+} // namespace
+
+ StatusWith<Date_t> dateFromISOString(const StringData& dateString) {
+ std::tm theTime;
+ int millis = 0;
+ int tzAdjSecs = 0;
+ Status status = parseTm(dateString, &theTime, &millis, &tzAdjSecs);
+ if (!status.isOK()) {
+ return StatusWith<Date_t>(ErrorCodes::BadValue, status.reason());
+ }
+
+ unsigned long long resultMillis = 0;
+
+#if defined(_WIN32)
+ SYSTEMTIME dateStruct;
+ dateStruct.wMilliseconds = millis;
+ dateStruct.wSecond = theTime.tm_sec;
+ dateStruct.wMinute = theTime.tm_min;
+ dateStruct.wHour = theTime.tm_hour;
+ dateStruct.wDay = theTime.tm_mday;
+ dateStruct.wDayOfWeek = -1; /* ignored */
+ dateStruct.wMonth = theTime.tm_mon + 1;
+ dateStruct.wYear = theTime.tm_year + 1900;
+
+ // Output parameter for SystemTimeToFileTime
+ FILETIME fileTime;
+
+ // the wDayOfWeek member of SYSTEMTIME is ignored by this function
+ if (SystemTimeToFileTime(&dateStruct, &fileTime) == 0) {
+ StringBuilder sb;
+ sb << "Error converting Windows system time to file time for date: " << dateString
+ << ". Error code: " << GetLastError();
+ return StatusWith<Date_t>(ErrorCodes::BadValue, sb.str());
+ }
+
+ // The Windows FILETIME structure contains two parts of a 64-bit value representing the
+ // number of 100-nanosecond intervals since January 1, 1601
+ unsigned long long windowsTimeOffset =
+ (static_cast<unsigned long long>(fileTime.dwHighDateTime) << 32) |
+ fileTime.dwLowDateTime;
+
+ // There are 11644473600 seconds between the unix epoch and the windows epoch
+ // 100-nanoseconds = milliseconds * 10000
+ unsigned long long epochDifference = 11644473600000 * 10000;
+
+ // removes the diff between 1970 and 1601
+ windowsTimeOffset -= epochDifference;
+
+ // 1 milliseconds = 1000000 nanoseconds = 10000 100-nanosecond intervals
+ resultMillis = windowsTimeOffset / 10000;
+#else
+ struct tm dateStruct = { 0 };
+ dateStruct.tm_sec = theTime.tm_sec;
+ dateStruct.tm_min = theTime.tm_min;
+ dateStruct.tm_hour = theTime.tm_hour;
+ dateStruct.tm_mday = theTime.tm_mday;
+ dateStruct.tm_mon = theTime.tm_mon;
+ dateStruct.tm_year = theTime.tm_year;
+ dateStruct.tm_wday = 0;
+ dateStruct.tm_yday = 0;
+
+ resultMillis = (1000 * static_cast<unsigned long long>(timegm(&dateStruct))) + millis;
+#endif
+
+ resultMillis += (tzAdjSecs * 1000);
+
+ return StatusWith<Date_t>(resultMillis);
+ }
+
+#undef MONGO_ISO_DATE_FMT_NO_TZ
+
+ void Date_t::toTm(tm* buf) {
+ time_t dtime = toTimeT();
+#if defined(_WIN32)
+ gmtime_s(buf, &dtime);
+#else
+ gmtime_r(&dtime, buf);
+#endif
+ }
+
+ std::string Date_t::toString() const {
+ return time_t_to_String(toTimeT());
+ }
+
+ time_t Date_t::toTimeT() const {
+ verify((long long)millis >= 0); // TODO when millis is signed, delete
+ verify(((long long)millis/1000) < (std::numeric_limits<time_t>::max)());
+ return millis / 1000;
+ }
+
+ std::string dateToCtimeString(Date_t date) {
+ time_t t = date.toTimeT();
+ char buf[64];
+#if defined(_WIN32)
+ ctime_s(buf, sizeof(buf), &t);
+#else
+ ctime_r(&t, buf);
+#endif
+ char* milliSecStr = buf + 19;
+ snprintf(milliSecStr, 5, ".%03d", static_cast<int32_t>(date.asInt64() % 1000));
+ return buf;
+ }
+
boost::gregorian::date currentDate() {
boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
return now.date();
@@ -208,6 +771,15 @@ namespace mongo {
unsigned long long lastErrorTimeMillis = _lastErrorTimeMillis;
_lastErrorTimeMillis = currTimeMillis;
+ lastSleepMillis = getNextSleepMillis(lastSleepMillis, currTimeMillis, lastErrorTimeMillis);
+
+ // Store the last slept time
+ _lastSleepMillis = lastSleepMillis;
+ sleepmillis( lastSleepMillis );
+ }
+
+ int Backoff::getNextSleepMillis(int lastSleepMillis, unsigned long long currTimeMillis,
+ unsigned long long lastErrorTimeMillis) const {
// Backoff logic
// Get the time since the last error
@@ -227,9 +799,7 @@ namespace mongo {
if( lastSleepMillis == 0 ) lastSleepMillis = 1;
else lastSleepMillis = std::min( lastSleepMillis * 2, _maxSleepMillis );
- // Store the last slept time
- _lastSleepMillis = lastSleepMillis;
- sleepmillis( lastSleepMillis );
+ return lastSleepMillis;
}
extern long long jsTime_virtual_skew;