Botan  2.1.0
Crypto and TLS for C++11
calendar.cpp
Go to the documentation of this file.
1 /*
2 * Calendar Functions
3 * (C) 1999-2010 Jack Lloyd
4 * (C) 2015 Simon Warta (Kullo GmbH)
5 *
6 * Botan is released under the Simplified BSD License (see license.txt)
7 */
8 
9 #include <botan/calendar.h>
10 #include <botan/exceptn.h>
11 #include <ctime>
12 #include <sstream>
13 #include <iomanip>
14 #include <botan/mutex.h>
15 #include <stdlib.h>
16 
17 #if defined(BOTAN_HAS_BOOST_DATETIME)
18 #include <boost/date_time/posix_time/posix_time_types.hpp>
19 #endif
20 
21 namespace Botan {
22 
23 namespace {
24 
25 std::tm do_gmtime(std::time_t time_val)
26  {
27  std::tm tm;
28 
29 #if defined(BOTAN_TARGET_OS_HAS_GMTIME_S)
30  gmtime_s(&tm, &time_val); // Windows
31 #elif defined(BOTAN_TARGET_OS_HAS_GMTIME_R)
32  gmtime_r(&time_val, &tm); // Unix/SUSv2
33 #else
34  std::tm* tm_p = std::gmtime(&time_val);
35  if (tm_p == nullptr)
36  throw Encoding_Error("time_t_to_tm could not convert");
37  tm = *tm_p;
38 #endif
39 
40  return tm;
41  }
42 
43 #if !defined(BOTAN_TARGET_OS_HAS_TIMEGM) && !(defined(BOTAN_TARGET_OS_HAS_MKGMTIME) && defined(BOTAN_BUILD_COMPILER_IS_MSVC))
44 
45 #if defined(BOTAN_HAS_BOOST_DATETIME)
46 
47 std::time_t boost_timegm(std::tm *tm)
48  {
49  const int sec = tm->tm_sec;
50  const int min = tm->tm_min;
51  const int hour = tm->tm_hour;
52  const int day = tm->tm_mday;
53  const int mon = tm->tm_mon + 1;
54  const int year = tm->tm_year + 1900;
55 
56  std::time_t out;
57 
58  {
59  using namespace boost::posix_time;
60  using namespace boost::gregorian;
61  const auto epoch = ptime(date(1970, 01, 01));
62  const auto time = ptime(date(year, mon, day),
63  hours(hour) + minutes(min) + seconds(sec));
64  const time_duration diff(time - epoch);
65  out = diff.ticks() / diff.ticks_per_second();
66  }
67 
68  return out;
69  }
70 
71 #elif defined(BOTAN_OS_TYPE_IS_UNIX)
72 
73 #pragma message "Caution! A fallback version of timegm() is used which is not thread-safe"
74 
75 mutex_type ENV_TZ;
76 
77 std::time_t fallback_timegm(std::tm *tm)
78  {
79  std::time_t out;
80  std::string tz_backup;
81 
82  ENV_TZ.lock();
83 
84  // Store current value of env variable TZ
85  const char* tz_env_pointer = ::getenv("TZ");
86  if (tz_env_pointer != nullptr)
87  tz_backup = std::string(tz_env_pointer);
88 
89  // Clear value of TZ
90  ::setenv("TZ", "", 1);
91  ::tzset();
92 
93  out = ::mktime(tm);
94 
95  // Restore TZ
96  if (!tz_backup.empty())
97  {
98  // setenv makes a copy of the second argument
99  ::setenv("TZ", tz_backup.data(), 1);
100  }
101  else
102  {
103  ::unsetenv("TZ");
104  }
105  ::tzset();
106 
107  ENV_TZ.unlock();
108 
109  return out;
110 }
111 #endif // BOTAN_HAS_BOOST_DATETIME
112 
113 #endif
114 
115 }
116 
117 std::chrono::system_clock::time_point calendar_point::to_std_timepoint() const
118  {
119  if (year < 1970)
120  throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years before 1970.");
121 
122  // 32 bit time_t ends at January 19, 2038
123  // https://msdn.microsoft.com/en-us/library/2093ets1.aspx
124  // Throw after 2037 if 32 bit time_t is used
125  if (year > 2037 && sizeof(std::time_t) == 4)
126  {
127  throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2037.");
128  }
129 
130  // std::tm: struct without any timezone information
131  std::tm tm;
132  tm.tm_isdst = -1; // i.e. no DST information available
133  tm.tm_sec = seconds;
134  tm.tm_min = minutes;
135  tm.tm_hour = hour;
136  tm.tm_mday = day;
137  tm.tm_mon = month - 1;
138  tm.tm_year = year - 1900;
139 
140  // Define a function alias `botan_timegm`
141  #if defined(BOTAN_TARGET_OS_HAS_TIMEGM)
142  std::time_t (&botan_timegm)(std::tm *tm) = timegm;
143  #elif defined(BOTAN_TARGET_OS_HAS_MKGMTIME) && defined(BOTAN_BUILD_COMPILER_IS_MSVC)
144  // http://stackoverflow.com/questions/16647819/timegm-cross-platform
145  std::time_t (&botan_timegm)(std::tm *tm) = _mkgmtime;
146  #elif defined(BOTAN_HAS_BOOST_DATETIME)
147  std::time_t (&botan_timegm)(std::tm *tm) = boost_timegm;
148  #elif defined(BOTAN_OS_TYPE_IS_UNIX)
149  std::time_t (&botan_timegm)(std::tm *tm) = fallback_timegm;
150  #else
151  std::time_t (&botan_timegm)(std::tm *tm) = mktime; // localtime instead...
152  #endif
153 
154  // Convert std::tm to std::time_t
155  std::time_t tt = botan_timegm(&tm);
156  if (tt == -1)
157  throw Invalid_Argument("calendar_point couldn't be converted: " + to_string());
158 
159  return std::chrono::system_clock::from_time_t(tt);
160  }
161 
162 std::string calendar_point::to_string() const
163  {
164  // desired format: <YYYY>-<MM>-<dd>T<HH>:<mm>:<ss>
165  std::stringstream output;
166  {
167  using namespace std;
168  output << setfill('0')
169  << setw(4) << year << "-" << setw(2) << month << "-" << setw(2) << day
170  << "T"
171  << setw(2) << hour << ":" << setw(2) << minutes << ":" << setw(2) << seconds;
172  }
173  return output.str();
174  }
175 
176 
178  const std::chrono::system_clock::time_point& time_point)
179  {
180  std::tm tm = do_gmtime(std::chrono::system_clock::to_time_t(time_point));
181 
182  return calendar_point(tm.tm_year + 1900,
183  tm.tm_mon + 1,
184  tm.tm_mday,
185  tm.tm_hour,
186  tm.tm_min,
187  tm.tm_sec);
188  }
189 
190 }
std::chrono::system_clock::time_point to_std_timepoint() const
Definition: calendar.cpp:117
std::string to_string() const
Definition: calendar.cpp:162
Definition: bigint.h:619
Definition: alg_id.cpp:13
calendar_point(uint32_t y, uint32_t mon, uint32_t d, uint32_t h, uint32_t min, uint32_t sec)
Definition: calendar.h:52
T min(T a, T b)
Definition: ct_utils.h:180
calendar_point calendar_value(const std::chrono::system_clock::time_point &time_point)
Definition: calendar.cpp:177