Kea 2.5.6
time_utilities.cc
Go to the documentation of this file.
1// Copyright (C) 2010-2016 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <stdint.h>
10
11#include <sys/time.h>
12
13#include <string>
14#include <iomanip>
15#include <iostream>
16#include <sstream>
17
18#include <stdio.h>
19#include <time.h>
20
22
23#include <util/time_utilities.h>
24
25using namespace std;
26
27namespace {
28int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
29
30inline bool
31isLeap(const int y) {
32 return ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0);
33}
34
35unsigned int
36yearSecs(const int year) {
37 return ((isLeap(year) ? 366 : 365 ) * 86400);
38}
39
40unsigned int
41monthSecs(const int month, const int year) {
42 return ((days[month] + ((month == 1 && isLeap(year)) ? 1 : 0 )) * 86400);
43}
44}
45
46namespace isc {
47namespace util {
48
49string
50timeToText64(uint64_t value) {
51 struct tm tm;
52 unsigned int secs;
53
54 // We cannot rely on gmtime() because time_t may not be of 64 bit
55 // integer. The following conversion logic is borrowed from BIND 9.
56 tm.tm_year = 70;
57 while ((secs = yearSecs(tm.tm_year + 1900)) <= value) {
58 value -= secs;
59 ++tm.tm_year;
60 if (tm.tm_year + 1900 > 9999) {
62 "Time value out of range (year > 9999): " <<
63 tm.tm_year + 1900);
64 }
65 }
66 tm.tm_mon = 0;
67 while ((secs = monthSecs(tm.tm_mon, tm.tm_year + 1900)) <= value) {
68 value -= secs;
69 tm.tm_mon++;
70 }
71 tm.tm_mday = 1;
72 while (86400 <= value) {
73 value -= 86400;
74 ++tm.tm_mday;
75 }
76 tm.tm_hour = 0;
77 while (3600 <= value) {
78 value -= 3600;
79 ++tm.tm_hour;
80 }
81 tm.tm_min = 0;
82 while (60 <= value) {
83 value -= 60;
84 ++tm.tm_min;
85 }
86 tm.tm_sec = value; // now t < 60, so this substitution is safe.
87
88 ostringstream oss;
89 oss << setfill('0')
90 << setw(4) << tm.tm_year + 1900
91 << setw(2) << tm.tm_mon + 1
92 << setw(2) << tm.tm_mday
93 << setw(2) << tm.tm_hour
94 << setw(2) << tm.tm_min
95 << setw(2) << tm.tm_sec;
96 return (oss.str());
97}
98
99// timeToText32() below uses the current system time. To test it with
100// unusual current time values we introduce the following function pointer;
101// when it's non NULL, we call it to get the (normally faked) current time.
102// Otherwise we use the standard gettimeofday(2). This hook is specifically
103// intended for testing purposes, so, even if it's visible outside of this
104// library, it's not even declared in a header file.
105namespace detail {
106int64_t (*gettimeFunction)() = NULL;
107
108int64_t
110 if (gettimeFunction != NULL) {
111 return (gettimeFunction());
112 }
113
114 struct timeval now;
115 gettimeofday(&now, NULL);
116
117 return (static_cast<int64_t>(now.tv_sec));
118}
119}
120
121string
122timeToText32(const uint32_t value) {
123 // We first adjust the time to the closest epoch based on the current time.
124 // Note that the following variables must be signed in order to handle
125 // time until year 2038 correctly.
126 const int64_t start = detail::gettimeWrapper() - 0x7fffffff;
127 int64_t base = 0;
128 int64_t t;
129 while ((t = (base + value)) < start) {
130 base += 0x100000000LL;
131 }
132
133 // Then convert it to text.
134 return (timeToText64(t));
135}
136
137namespace {
138const size_t DATE_LEN = 14; // YYYYMMDDHHmmSS
139
140inline uint64_t ull(const int c) { return (static_cast<uint64_t>(c)); }
141
142inline void
143checkRange(const unsigned min, const unsigned max, const unsigned value,
144 const string& valname)
145{
146 if ((value >= min) && (value <= max)) {
147 return;
148 }
149 isc_throw(InvalidTime, "Invalid " << valname << " value: " << value);
150}
151}
152
153uint64_t
154timeFromText64(const string& time_txt) {
155 // Confirm the source only consists digits. sscanf() allows some
156 // minor exceptions.
157 for (string::size_type i = 0; i < time_txt.length(); ++i) {
158 if (!isdigit(time_txt.at(i))) {
159 isc_throw(InvalidTime, "Couldn't convert non-numeric time value: "
160 << time_txt);
161 }
162 }
163
164 unsigned year, month, day, hour, minute, second;
165 if (time_txt.length() != DATE_LEN ||
166 sscanf(time_txt.c_str(), "%4u%2u%2u%2u%2u%2u",
167 &year, &month, &day, &hour, &minute, &second) != 6)
168 {
169 isc_throw(InvalidTime, "Couldn't convert time value: " << time_txt);
170 }
171
172 checkRange(1970, 9999, year, "year");
173 checkRange(1, 12, month, "month");
174 checkRange(1, days[month - 1] + ((month == 2 && isLeap(year)) ? 1 : 0),
175 day, "day");
176 checkRange(0, 23, hour, "hour");
177 checkRange(0, 59, minute, "minute");
178 checkRange(0, 60, second, "second"); // 60 == leap second.
179
180 uint64_t timeval = second + (ull(60) * minute) + (ull(3600) * hour) +
181 ((day - 1) * ull(86400));
182 for (unsigned m = 0; m < (month - 1); ++m) {
183 timeval += days[m] * ull(86400);
184 }
185 if (isLeap(year) && month > 2) {
186 timeval += ull(86400);
187 }
188 for (unsigned y = 1970; y < year; ++y) {
189 timeval += ((isLeap(y) ? 366 : 365) * ull(86400));
190 }
191
192 return (timeval);
193}
194
195uint32_t
196timeFromText32(const string& time_txt) {
197 // The implicit conversion from uint64_t to uint32_t should just work here,
198 // because we only need to drop higher 32 bits.
199 return (timeFromText64(time_txt));
200}
201
202}
203}
A standard DNS (or ISC) module exception that is thrown if a time conversion function encounters bad ...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
int64_t(* gettimeFunction)()
int64_t gettimeWrapper()
Return the current time in seconds.
uint32_t timeFromText32(const string &time_txt)
Convert textual DNSSEC time to integer, 32-bit version.
string timeToText32(const uint32_t value)
Convert integral DNSSEC time to textual form, 32-bit version.
string timeToText64(uint64_t value)
Convert integral DNSSEC time to textual form, 64-bit version.
uint64_t timeFromText64(const string &time_txt)
Convert textual DNSSEC time to integer, 64-bit version.
Defines the logger used by the top-level component of kea-lfc.