Kea  2.1.7-git
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 
21 #include <exceptions/exceptions.h>
22 
23 #include <util/time_utilities.h>
24 
25 using namespace std;
26 
27 namespace {
28 int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
29 
30 inline bool
31 isLeap(const int y) {
32  return ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0);
33 }
34 
35 unsigned int
36 yearSecs(const int year) {
37  return ((isLeap(year) ? 366 : 365 ) * 86400);
38 }
39 
40 unsigned int
41 monthSecs(const int month, const int year) {
42  return ((days[month] + ((month == 1 && isLeap(year)) ? 1 : 0 )) * 86400);
43 }
44 }
45 
46 namespace isc {
47 namespace util {
48 
49 string
50 timeToText64(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.
105 namespace detail {
106 int64_t (*gettimeFunction)() = NULL;
107 
108 int64_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 
121 string
122 timeToText32(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 
137 namespace {
138 const size_t DATE_LEN = 14; // YYYYMMDDHHmmSS
139 
140 inline uint64_t ull(const int c) { return (static_cast<uint64_t>(c)); }
141 
142 inline void
143 checkRange(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 
153 uint64_t
154 timeFromText64(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 
195 uint32_t
196 timeFromText32(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 }
int64_t gettimeWrapper()
Return the current time in seconds.
A standard DNS (or ISC) module exception that is thrown if a time conversion function encounters bad ...
uint64_t timeFromText64(const string &time_txt)
Convert textual DNSSEC time to integer, 64-bit version.
string timeToText32(const uint32_t value)
Convert integral DNSSEC time to textual form, 32-bit version.
STL namespace.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
uint32_t timeFromText32(const string &time_txt)
Convert textual DNSSEC time to integer, 32-bit version.
Defines the logger used by the top-level component of kea-lfc.
string timeToText64(uint64_t value)
Convert integral DNSSEC time to textual form, 64-bit version.
int64_t(* gettimeFunction)()