time_utilities.cc 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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. #include <stdint.h>
  15. #include <sys/time.h>
  16. #include <string>
  17. #include <iomanip>
  18. #include <iostream>
  19. #include <sstream>
  20. #include <stdio.h>
  21. #include <time.h>
  22. #include <exceptions/exceptions.h>
  23. #include <util/time_utilities.h>
  24. using namespace std;
  25. namespace {
  26. int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  27. inline bool
  28. isLeap(const int y) {
  29. return ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0);
  30. }
  31. unsigned int
  32. yearSecs(const int year) {
  33. return ((isLeap(year) ? 366 : 365 ) * 86400);
  34. }
  35. unsigned int
  36. monthSecs(const int month, const int year) {
  37. return ((days[month] + ((month == 1 && isLeap(year)) ? 1 : 0 )) * 86400);
  38. }
  39. }
  40. namespace isc {
  41. namespace util {
  42. string
  43. timeToText64(uint64_t value) {
  44. struct tm tm;
  45. unsigned int secs;
  46. // We cannot rely on gmtime() because time_t may not be of 64 bit
  47. // integer. The following conversion logic is borrowed from BIND 9.
  48. tm.tm_year = 70;
  49. while ((secs = yearSecs(tm.tm_year + 1900)) <= value) {
  50. value -= secs;
  51. ++tm.tm_year;
  52. if (tm.tm_year + 1900 > 9999) {
  53. isc_throw(InvalidTime,
  54. "Time value out of range (year > 9999): " <<
  55. tm.tm_year + 1900);
  56. }
  57. }
  58. tm.tm_mon = 0;
  59. while ((secs = monthSecs(tm.tm_mon, tm.tm_year + 1900)) <= value) {
  60. value -= secs;
  61. tm.tm_mon++;
  62. }
  63. tm.tm_mday = 1;
  64. while (86400 <= value) {
  65. value -= 86400;
  66. ++tm.tm_mday;
  67. }
  68. tm.tm_hour = 0;
  69. while (3600 <= value) {
  70. value -= 3600;
  71. ++tm.tm_hour;
  72. }
  73. tm.tm_min = 0;
  74. while (60 <= value) {
  75. value -= 60;
  76. ++tm.tm_min;
  77. }
  78. tm.tm_sec = value; // now t < 60, so this substitution is safe.
  79. ostringstream oss;
  80. oss << setfill('0')
  81. << setw(4) << tm.tm_year + 1900
  82. << setw(2) << tm.tm_mon + 1
  83. << setw(2) << tm.tm_mday
  84. << setw(2) << tm.tm_hour
  85. << setw(2) << tm.tm_min
  86. << setw(2) << tm.tm_sec;
  87. return (oss.str());
  88. }
  89. // timeToText32() below uses the current system time. To test it with
  90. // unusual current time values we introduce the following function pointer;
  91. // when it's non NULL, we call it to get the (normally faked) current time.
  92. // Otherwise we use the standard gettimeofday(2). This hook is specifically
  93. // intended for testing purposes, so, even if it's visible outside of this
  94. // library, it's not even declared in a header file.
  95. namespace detail {
  96. int64_t (*gettimeFunction)() = NULL;
  97. int64_t
  98. gettimeWrapper() {
  99. if (gettimeFunction != NULL) {
  100. return (gettimeFunction());
  101. }
  102. struct timeval now;
  103. gettimeofday(&now, NULL);
  104. return (static_cast<int64_t>(now.tv_sec));
  105. }
  106. }
  107. string
  108. timeToText32(const uint32_t value) {
  109. // We first adjust the time to the closest epoch based on the current time.
  110. // Note that the following variables must be signed in order to handle
  111. // time until year 2038 correctly.
  112. const int64_t start = detail::gettimeWrapper() - 0x7fffffff;
  113. int64_t base = 0;
  114. int64_t t;
  115. while ((t = (base + value)) < start) {
  116. base += 0x100000000LL;
  117. }
  118. // Then convert it to text.
  119. return (timeToText64(t));
  120. }
  121. namespace {
  122. const size_t DATE_LEN = 14; // YYYYMMDDHHmmSS
  123. inline void
  124. checkRange(const int min, const int max, const int value,
  125. const string& valname)
  126. {
  127. if ((value >= min) && (value <= max)) {
  128. return;
  129. }
  130. isc_throw(InvalidTime, "Invalid " << valname << "value: " << value);
  131. }
  132. }
  133. uint64_t
  134. timeFromText64(const string& time_txt) {
  135. // Confirm the source only consists digits. sscanf() allows some
  136. // minor exceptions.
  137. for (string::size_type i = 0; i < time_txt.length(); ++i) {
  138. if (!isdigit(time_txt.at(i))) {
  139. isc_throw(InvalidTime, "Couldn't convert non-numeric time value: "
  140. << time_txt);
  141. }
  142. }
  143. int year, month, day, hour, minute, second;
  144. if (time_txt.length() != DATE_LEN ||
  145. sscanf(time_txt.c_str(), "%4d%2d%2d%2d%2d%2d",
  146. &year, &month, &day, &hour, &minute, &second) != 6)
  147. {
  148. isc_throw(InvalidTime, "Couldn't convert time value: " << time_txt);
  149. }
  150. checkRange(1970, 9999, year, "year");
  151. checkRange(1, 12, month, "month");
  152. checkRange(1, days[month - 1] + ((month == 2 && isLeap(year)) ? 1 : 0),
  153. day, "day");
  154. checkRange(0, 23, hour, "hour");
  155. checkRange(0, 59, minute, "minute");
  156. checkRange(0, 60, second, "second"); // 60 == leap second.
  157. uint64_t timeval = second + (60 * minute) + (3600 * hour) +
  158. ((day - 1) * 86400);
  159. for (int m = 0; m < (month - 1); ++m) {
  160. timeval += days[m] * 86400;
  161. }
  162. if (isLeap(year) && month > 2) {
  163. timeval += 86400;
  164. }
  165. for (int y = 1970; y < year; ++y) {
  166. timeval += ((isLeap(y) ? 366 : 365 ) * 86400);
  167. }
  168. return (timeval);
  169. }
  170. uint32_t
  171. timeFromText32(const string& time_txt) {
  172. // The implicit conversion from uint64_t to uint32_t should just work here,
  173. // because we only need to drop higher 32 bits.
  174. return (timeFromText64(time_txt));
  175. }
  176. }
  177. }