Branch data Line data Source code
1 : : // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #include <stdint.h>
16 : :
17 : : #include <sys/time.h>
18 : :
19 : : #include <string>
20 : : #include <iomanip>
21 : : #include <iostream>
22 : : #include <sstream>
23 : :
24 : : #include <stdio.h>
25 : : #include <time.h>
26 : :
27 : : #include <exceptions/exceptions.h>
28 : :
29 : : #include <util/time_utilities.h>
30 : :
31 : : using namespace std;
32 : :
33 : : namespace {
34 : : int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
35 : :
36 : : inline bool
37 : : isLeap(const int y) {
38 [ + + ][ + + ]: 555679 : return ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0);
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + - ]
[ + + ]
39 : : }
40 : :
41 : : unsigned int
42 : : yearSecs(const int year) {
43 [ + + ]: 82699 : return ((isLeap(year) ? 366 : 365 ) * 86400);
44 : : }
45 : :
46 : : unsigned int
47 : : monthSecs(const int month, const int year) {
48 [ + + ][ + + ]: 3966 : return ((days[month] + ((month == 1 && isLeap(year)) ? 1 : 0 )) * 86400);
49 : : }
50 : : }
51 : :
52 : : namespace isc {
53 : : namespace util {
54 : :
55 : : string
56 : 1503 : timeToText64(uint64_t value) {
57 : : struct tm tm;
58 : : unsigned int secs;
59 : :
60 : : // We cannot rely on gmtime() because time_t may not be of 64 bit
61 : : // integer. The following conversion logic is borrowed from BIND 9.
62 : 1503 : tm.tm_year = 70;
63 [ + + ]: 84202 : while ((secs = yearSecs(tm.tm_year + 1900)) <= value) {
64 : 81198 : value -= secs;
65 : 81198 : ++tm.tm_year;
66 [ + + ]: 81198 : if (tm.tm_year + 1900 > 9999) {
67 [ + - ][ + - ]: 4 : isc_throw(InvalidTime,
[ + - ]
68 : : "Time value out of range (year > 9999): " <<
69 : : tm.tm_year + 1900);
70 : : }
71 : : }
72 : 1501 : tm.tm_mon = 0;
73 [ + + ]: 4534 : while ((secs = monthSecs(tm.tm_mon, tm.tm_year + 1900)) <= value) {
74 : 1532 : value -= secs;
75 : 1532 : tm.tm_mon++;
76 : : }
77 : 1501 : tm.tm_mday = 1;
78 [ + + ]: 8787 : while (86400 <= value) {
79 : 7286 : value -= 86400;
80 : 7286 : ++tm.tm_mday;
81 : : }
82 : 1501 : tm.tm_hour = 0;
83 [ + + ]: 5115 : while (3600 <= value) {
84 : 3614 : value -= 3600;
85 : 3614 : ++tm.tm_hour;
86 : : }
87 : 1501 : tm.tm_min = 0;
88 [ + + ]: 16452 : while (60 <= value) {
89 : 14951 : value -= 60;
90 : 14951 : ++tm.tm_min;
91 : : }
92 : 1501 : tm.tm_sec = value; // now t < 60, so this substitution is safe.
93 : :
94 : 3002 : ostringstream oss;
95 [ + - ]: 1501 : oss << setfill('0')
96 [ + - ]: 1501 : << setw(4) << tm.tm_year + 1900
97 [ + - ]: 1501 : << setw(2) << tm.tm_mon + 1
98 [ + - ]: 1501 : << setw(2) << tm.tm_mday
99 [ + - ]: 1501 : << setw(2) << tm.tm_hour
100 [ + - ]: 1501 : << setw(2) << tm.tm_min
101 [ + - ]: 1501 : << setw(2) << tm.tm_sec;
102 : 1501 : return (oss.str());
103 : : }
104 : :
105 : : // timeToText32() below uses the current system time. To test it with
106 : : // unusual current time values we introduce the following function pointer;
107 : : // when it's non NULL, we call it to get the (normally faked) current time.
108 : : // Otherwise we use the standard gettimeofday(2). This hook is specifically
109 : : // intended for testing purposes, so, even if it's visible outside of this
110 : : // library, it's not even declared in a header file.
111 : : namespace detail {
112 : : int64_t (*gettimeFunction)() = NULL;
113 : :
114 : : int64_t
115 : 1775 : gettimeWrapper() {
116 [ + + ]: 1775 : if (gettimeFunction != NULL) {
117 : 115 : return (gettimeFunction());
118 : : }
119 : :
120 : : struct timeval now;
121 : 1660 : gettimeofday(&now, NULL);
122 : :
123 : 1775 : return (static_cast<int64_t>(now.tv_sec));
124 : : }
125 : : }
126 : :
127 : : string
128 : 1499 : timeToText32(const uint32_t value) {
129 : : // We first adjust the time to the closest epoch based on the current time.
130 : : // Note that the following variables must be signed in order to handle
131 : : // time until year 2038 correctly.
132 : 1499 : const int64_t start = detail::gettimeWrapper() - 0x7fffffff;
133 : 1499 : int64_t base = 0;
134 : : int64_t t;
135 [ + + ]: 1616 : while ((t = (base + value)) < start) {
136 : 117 : base += 0x100000000LL;
137 : : }
138 : :
139 : : // Then convert it to text.
140 : 1499 : return (timeToText64(t));
141 : : }
142 : :
143 : : namespace {
144 : : const size_t DATE_LEN = 14; // YYYYMMDDHHmmSS
145 : :
146 : : inline void
147 : 85955 : checkRange(const int min, const int max, const int value,
148 : : const string& valname)
149 : : {
150 [ + + ]: 85955 : if ((value >= min) && (value <= max)) {
151 : 85944 : return;
152 : : }
153 [ + - ][ + - ]: 22 : isc_throw(InvalidTime, "Invalid " << valname << "value: " << value);
[ + - ][ + - ]
[ + - ]
154 : : }
155 : : }
156 : :
157 : : uint64_t
158 : 14335 : timeFromText64(const string& time_txt) {
159 : : // Confirm the source only consists digits. sscanf() allows some
160 : : // minor exceptions.
161 [ + + ]: 215001 : for (string::size_type i = 0; i < time_txt.length(); ++i) {
162 [ + + ]: 200668 : if (!isdigit(time_txt.at(i))) {
163 [ + - ][ + - ]: 4 : isc_throw(InvalidTime, "Couldn't convert non-numeric time value: "
[ + - ]
164 : : << time_txt);
165 : : }
166 : : }
167 : :
168 : : int year, month, day, hour, minute, second;
169 [ + + ][ - + ]: 14333 : if (time_txt.length() != DATE_LEN ||
[ + + ]
170 : : sscanf(time_txt.c_str(), "%4d%2d%2d%2d%2d%2d",
171 : 14331 : &year, &month, &day, &hour, &minute, &second) != 6)
172 : : {
173 [ + - ][ + - ]: 4 : isc_throw(InvalidTime, "Couldn't convert time value: " << time_txt);
[ + - ]
174 : : }
175 : :
176 [ + + ]: 14331 : checkRange(1970, 9999, year, "year");
177 [ + + ]: 14330 : checkRange(1, 12, month, "month");
178 : 20990 : checkRange(1, days[month - 1] + ((month == 2 && isLeap(year)) ? 1 : 0),
179 [ + + ][ + + ]: 20990 : day, "day");
[ + + ]
180 [ + + ]: 14323 : checkRange(0, 23, hour, "hour");
181 [ + + ]: 14322 : checkRange(0, 59, minute, "minute");
182 [ + + ]: 14321 : checkRange(0, 60, second, "second"); // 60 == leap second.
183 : :
184 : : uint64_t timeval = second + (60 * minute) + (3600 * hour) +
185 : 14320 : ((day - 1) * 86400);
186 [ + + ]: 28064 : for (int m = 0; m < (month - 1); ++m) {
187 : 13744 : timeval += days[m] * 86400;
188 : : }
189 [ + + ][ + + ]: 14320 : if (isLeap(year) && month > 2) {
[ + + ]
190 : 14320 : timeval += 86400;
191 : : }
192 [ + + ]: 465385 : for (int y = 1970; y < year; ++y) {
193 [ + + ]: 451065 : timeval += ((isLeap(y) ? 366 : 365 ) * 86400);
194 : : }
195 : :
196 : 14320 : return (timeval);
197 : : }
198 : :
199 : : uint32_t
200 : 14333 : timeFromText32(const string& time_txt) {
201 : : // The implicit conversion from uint64_t to uint32_t should just work here,
202 : : // because we only need to drop higher 32 bits.
203 : 14333 : return (timeFromText64(time_txt));
204 : : }
205 : :
206 : : }
207 : 169 : }
|