summaryrefslogtreecommitdiffstats
path: root/src/lib/util/time_utilities.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/time_utilities.cc')
-rw-r--r--src/lib/util/time_utilities.cc203
1 files changed, 203 insertions, 0 deletions
diff --git a/src/lib/util/time_utilities.cc b/src/lib/util/time_utilities.cc
new file mode 100644
index 0000000..5da1db7
--- /dev/null
+++ b/src/lib/util/time_utilities.cc
@@ -0,0 +1,203 @@
+// Copyright (C) 2010-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <stdint.h>
+
+#include <sys/time.h>
+
+#include <string>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include <stdio.h>
+#include <time.h>
+
+#include <exceptions/exceptions.h>
+
+#include <util/time_utilities.h>
+
+using namespace std;
+
+namespace {
+int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+inline bool
+isLeap(const int y) {
+ return ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0);
+}
+
+unsigned int
+yearSecs(const int year) {
+ return ((isLeap(year) ? 366 : 365 ) * 86400);
+}
+
+unsigned int
+monthSecs(const int month, const int year) {
+ return ((days[month] + ((month == 1 && isLeap(year)) ? 1 : 0 )) * 86400);
+}
+}
+
+namespace isc {
+namespace util {
+
+string
+timeToText64(uint64_t value) {
+ struct tm tm;
+ unsigned int secs;
+
+ // We cannot rely on gmtime() because time_t may not be of 64 bit
+ // integer. The following conversion logic is borrowed from BIND 9.
+ tm.tm_year = 70;
+ while ((secs = yearSecs(tm.tm_year + 1900)) <= value) {
+ value -= secs;
+ ++tm.tm_year;
+ if (tm.tm_year + 1900 > 9999) {
+ isc_throw(InvalidTime,
+ "Time value out of range (year > 9999): " <<
+ tm.tm_year + 1900);
+ }
+ }
+ tm.tm_mon = 0;
+ while ((secs = monthSecs(tm.tm_mon, tm.tm_year + 1900)) <= value) {
+ value -= secs;
+ tm.tm_mon++;
+ }
+ tm.tm_mday = 1;
+ while (86400 <= value) {
+ value -= 86400;
+ ++tm.tm_mday;
+ }
+ tm.tm_hour = 0;
+ while (3600 <= value) {
+ value -= 3600;
+ ++tm.tm_hour;
+ }
+ tm.tm_min = 0;
+ while (60 <= value) {
+ value -= 60;
+ ++tm.tm_min;
+ }
+ tm.tm_sec = value; // now t < 60, so this substitution is safe.
+
+ ostringstream oss;
+ oss << setfill('0')
+ << setw(4) << tm.tm_year + 1900
+ << setw(2) << tm.tm_mon + 1
+ << setw(2) << tm.tm_mday
+ << setw(2) << tm.tm_hour
+ << setw(2) << tm.tm_min
+ << setw(2) << tm.tm_sec;
+ return (oss.str());
+}
+
+// timeToText32() below uses the current system time. To test it with
+// unusual current time values we introduce the following function pointer;
+// when it's non NULL, we call it to get the (normally faked) current time.
+// Otherwise we use the standard gettimeofday(2). This hook is specifically
+// intended for testing purposes, so, even if it's visible outside of this
+// library, it's not even declared in a header file.
+namespace detail {
+int64_t (*gettimeFunction)() = NULL;
+
+int64_t
+gettimeWrapper() {
+ if (gettimeFunction != NULL) {
+ return (gettimeFunction());
+ }
+
+ struct timeval now;
+ gettimeofday(&now, NULL);
+
+ return (static_cast<int64_t>(now.tv_sec));
+}
+}
+
+string
+timeToText32(const uint32_t value) {
+ // We first adjust the time to the closest epoch based on the current time.
+ // Note that the following variables must be signed in order to handle
+ // time until year 2038 correctly.
+ const int64_t start = detail::gettimeWrapper() - 0x7fffffff;
+ int64_t base = 0;
+ int64_t t;
+ while ((t = (base + value)) < start) {
+ base += 0x100000000LL;
+ }
+
+ // Then convert it to text.
+ return (timeToText64(t));
+}
+
+namespace {
+const size_t DATE_LEN = 14; // YYYYMMDDHHmmSS
+
+inline uint64_t ull(const int c) { return (static_cast<uint64_t>(c)); }
+
+inline void
+checkRange(const unsigned min, const unsigned max, const unsigned value,
+ const string& valname)
+{
+ if ((value >= min) && (value <= max)) {
+ return;
+ }
+ isc_throw(InvalidTime, "Invalid " << valname << " value: " << value);
+}
+}
+
+uint64_t
+timeFromText64(const string& time_txt) {
+ // Confirm the source only consists digits. sscanf() allows some
+ // minor exceptions.
+ for (string::size_type i = 0; i < time_txt.length(); ++i) {
+ if (!isdigit(time_txt.at(i))) {
+ isc_throw(InvalidTime, "Couldn't convert non-numeric time value: "
+ << time_txt);
+ }
+ }
+
+ unsigned year, month, day, hour, minute, second;
+ if (time_txt.length() != DATE_LEN ||
+ sscanf(time_txt.c_str(), "%4u%2u%2u%2u%2u%2u",
+ &year, &month, &day, &hour, &minute, &second) != 6)
+ {
+ isc_throw(InvalidTime, "Couldn't convert time value: " << time_txt);
+ }
+
+ checkRange(1970, 9999, year, "year");
+ checkRange(1, 12, month, "month");
+ checkRange(1, days[month - 1] + ((month == 2 && isLeap(year)) ? 1 : 0),
+ day, "day");
+ checkRange(0, 23, hour, "hour");
+ checkRange(0, 59, minute, "minute");
+ checkRange(0, 60, second, "second"); // 60 == leap second.
+
+ uint64_t timeval = second + (ull(60) * minute) + (ull(3600) * hour) +
+ ((day - 1) * ull(86400));
+ for (unsigned m = 0; m < (month - 1); ++m) {
+ timeval += days[m] * ull(86400);
+ }
+ if (isLeap(year) && month > 2) {
+ timeval += ull(86400);
+ }
+ for (unsigned y = 1970; y < year; ++y) {
+ timeval += ((isLeap(y) ? 366 : 365) * ull(86400));
+ }
+
+ return (timeval);
+}
+
+uint32_t
+timeFromText32(const string& time_txt) {
+ // The implicit conversion from uint64_t to uint32_t should just work here,
+ // because we only need to drop higher 32 bits.
+ return (timeFromText64(time_txt));
+}
+
+}
+}