Botan 3.13.0
Crypto and TLS for C&
calendar.cpp
Go to the documentation of this file.
1/*
2* Calendar Functions
3* (C) 1999-2010,2017 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/internal/calendar.h>
10
11#include <botan/assert.h>
12#include <botan/exceptn.h>
13#include <array>
14
15namespace Botan {
16
17namespace {
18
19/*
20Portable replacement for timegm, _mkgmtime, etc
21
22Algorithm due to Howard Hinnant
23
24See https://howardhinnant.github.io/date_algorithms.html#days_from_civil
25for details and explanation. The result is negative for dates before the epoch.
26The code is slightly simplified by our assumption that the date is at least 1950,
27which is sufficient for our purposes (ASN1_Time uses the same lower bound).
28*/
29int64_t days_since_epoch(uint32_t year, uint32_t month, uint32_t day) {
30 BOTAN_ARG_CHECK(year >= 1950, "Years before 1950 not supported");
31
32 if(month <= 2) {
33 year -= 1;
34 }
35 const uint32_t era = year / 400;
36 const uint32_t yoe = year - era * 400; // [0, 399]
37 const uint32_t doy = (153 * (month + (month > 2 ? -3 : 9)) + 2) / 5 + day - 1; // [0, 365]
38 const uint32_t doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096]
39 return static_cast<int64_t>(era) * 146097 + static_cast<int64_t>(doe) - 719468;
40}
41
42/*
43Portable replacement for gmtime, gmtime_r, _gmtime_s, etc
44
45Algorithm due to Howard Hinnant
46
47See https://howardhinnant.github.io/date_algorithms.html#civil_from_days
48for details and explanation.
49*/
50std::array<uint32_t, 6> civil_from_time_point(const std::chrono::system_clock::time_point& tp) {
51 const int64_t t = static_cast<int64_t>(std::chrono::system_clock::to_time_t(tp));
52
53 // Split into days since epoch and seconds within the day, flooring towards
54 // negative infinity so that times before the epoch are handled correctly.
55 int64_t days = t / 86400;
56 int64_t tod = t % 86400;
57 if(tod < 0) {
58 tod += 86400;
59 days -= 1;
60 }
61
62 const int64_t z = days + 719468;
63 const int64_t era = (z >= 0 ? z : z - 146096) / 146097;
64 const int64_t doe = z - era * 146097; // [0, 146096]
65 const int64_t yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; // [0, 399]
66 const int64_t y = yoe + era * 400;
67 const int64_t doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365]
68 const int64_t mp = (5 * doy + 2) / 153; // [0, 11]
69 const int64_t day = doy - (153 * mp + 2) / 5 + 1; // [1, 31]
70 const int64_t month = mp < 10 ? mp + 3 : mp - 9; // [1, 12]
71 const int64_t year = y + (month <= 2 ? 1 : 0);
72
73 return {static_cast<uint32_t>(year),
74 static_cast<uint32_t>(month),
75 static_cast<uint32_t>(day),
76 static_cast<uint32_t>(tod / 3600),
77 static_cast<uint32_t>((tod % 3600) / 60),
78 static_cast<uint32_t>(tod % 60)};
79}
80
81} // namespace
82
83calendar_point::calendar_point(uint32_t y, uint32_t mon, uint32_t d, uint32_t h, uint32_t min, uint32_t sec) :
84 m_year(static_cast<uint16_t>(y)),
85 m_month(static_cast<uint8_t>(mon)),
86 m_day(static_cast<uint8_t>(d)),
87 m_hour(static_cast<uint8_t>(h)),
88 m_minutes(static_cast<uint8_t>(min)),
89 m_seconds(static_cast<uint8_t>(sec)) {
90 BOTAN_ARG_CHECK(y <= 9999, "Year is outside representable range");
91 BOTAN_ARG_CHECK(mon >= 1 && mon <= 12, "Month is outside range");
92 BOTAN_ARG_CHECK(d >= 1 && d <= 31, "Day is outside range");
93 BOTAN_ARG_CHECK(h < 24, "Hour is outside range");
94 BOTAN_ARG_CHECK(min < 60, "Minute is outside range");
95 BOTAN_ARG_CHECK(sec < 60, "Seconds is outside range");
96}
97
99 return (days_since_epoch(year(), month(), day()) * 86400) + (hour() * 60 * 60) + (minutes() * 60) + seconds();
100}
101
102std::chrono::system_clock::time_point calendar_point::to_std_timepoint() const {
103 const int64_t seconds_64 = this->seconds_since_epoch();
104
105 /*
106 * The tick of a system_clock varies by implementation, and so also the
107 * largest and smallest representable values vary. Ensure this date is within
108 * range of the clock implementation.
109 */
110 constexpr int64_t max_representable_seconds = static_cast<int64_t>(
111 std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::duration::max()).count());
112 constexpr int64_t min_representable_seconds = static_cast<int64_t>(
113 std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::duration::min()).count());
114
115 if(seconds_64 > max_representable_seconds || seconds_64 < min_representable_seconds) {
116 throw Invalid_Argument("calendar_point::to_std_timepoint time is outside the representable range");
117 }
118
119 const time_t seconds_time_t = static_cast<time_t>(seconds_64);
120
121 if(seconds_64 - seconds_time_t != 0) {
122 throw Invalid_Argument("calendar_point::to_std_timepoint time is outside the representable range");
123 }
124
125 return std::chrono::system_clock::from_time_t(seconds_time_t);
126}
127
128calendar_point::calendar_point(const std::chrono::system_clock::time_point& time_point) {
129 const auto [year, month, day, hour, minute, second] = civil_from_time_point(time_point);
130
131 BOTAN_ARG_CHECK(year <= 9999, "Year is outside representable range");
132
133 m_year = static_cast<uint16_t>(year);
134 m_month = static_cast<uint8_t>(month);
135 m_day = static_cast<uint8_t>(day);
136 m_hour = static_cast<uint8_t>(hour);
137 m_minutes = static_cast<uint8_t>(minute);
138 m_seconds = static_cast<uint8_t>(second);
139}
140
141} // namespace Botan
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
int64_t seconds_since_epoch() const
Definition calendar.cpp:98
uint32_t hour() const
Definition calendar.h:32
uint32_t seconds() const
Definition calendar.h:38
uint32_t day() const
Definition calendar.h:29
std::chrono::system_clock::time_point to_std_timepoint() const
Definition calendar.cpp:102
calendar_point(uint32_t y, uint32_t mon, uint32_t d, uint32_t h, uint32_t min, uint32_t sec)
Definition calendar.cpp:83
uint32_t minutes() const
Definition calendar.h:35
uint32_t month() const
Definition calendar.h:26
uint32_t year() const
Definition calendar.h:23