/src/cctz/include/cctz/civil_time_detail.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2016 Google Inc. All Rights Reserved. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #ifndef CCTZ_CIVIL_TIME_DETAIL_H_ |
16 | | #define CCTZ_CIVIL_TIME_DETAIL_H_ |
17 | | |
18 | | #include <cstdint> |
19 | | #include <limits> |
20 | | #include <ostream> |
21 | | #include <type_traits> |
22 | | |
23 | | // Disable constexpr support unless we are in C++14 mode. |
24 | | #if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910) |
25 | 1.13M | #define CONSTEXPR_D constexpr // data |
26 | | #define CONSTEXPR_F constexpr // function |
27 | | #define CONSTEXPR_M constexpr // member |
28 | | #else |
29 | | #define CONSTEXPR_D const |
30 | | #define CONSTEXPR_F inline |
31 | | #define CONSTEXPR_M |
32 | | #endif |
33 | | |
34 | | namespace cctz { |
35 | | |
36 | | // Support years that at least span the range of 64-bit time_t values. |
37 | | using year_t = std::int_fast64_t; |
38 | | |
39 | | // Type alias that indicates an argument is not normalized (e.g., the |
40 | | // constructor parameters and operands/results of addition/subtraction). |
41 | | using diff_t = std::int_fast64_t; |
42 | | |
43 | | namespace detail { |
44 | | |
45 | | // Type aliases that indicate normalized argument values. |
46 | | using month_t = std::int_fast8_t; // [1:12] |
47 | | using day_t = std::int_fast8_t; // [1:31] |
48 | | using hour_t = std::int_fast8_t; // [0:23] |
49 | | using minute_t = std::int_fast8_t; // [0:59] |
50 | | using second_t = std::int_fast8_t; // [0:59] |
51 | | |
52 | | // Normalized civil-time fields: Y-M-D HH:MM:SS. |
53 | | struct fields { |
54 | | CONSTEXPR_M fields(year_t year, month_t month, day_t day, |
55 | | hour_t hour, minute_t minute, second_t second) |
56 | 564k | : y(year), m(month), d(day), hh(hour), mm(minute), ss(second) {} |
57 | | std::int_least64_t y; |
58 | | std::int_least8_t m; |
59 | | std::int_least8_t d; |
60 | | std::int_least8_t hh; |
61 | | std::int_least8_t mm; |
62 | | std::int_least8_t ss; |
63 | | }; |
64 | | |
65 | | struct second_tag {}; |
66 | | struct minute_tag : second_tag {}; |
67 | | struct hour_tag : minute_tag {}; |
68 | | struct day_tag : hour_tag {}; |
69 | | struct month_tag : day_tag {}; |
70 | | struct year_tag : month_tag {}; |
71 | | |
72 | | //////////////////////////////////////////////////////////////////////// |
73 | | |
74 | | // Field normalization (without avoidable overflow). |
75 | | |
76 | | namespace impl { |
77 | | |
78 | 523k | CONSTEXPR_F bool is_leap_year(year_t y) noexcept { |
79 | 523k | return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); |
80 | 523k | } |
81 | 144k | CONSTEXPR_F int year_index(year_t y, month_t m) noexcept { |
82 | 144k | const int yi = static_cast<int>((y + (m > 2)) % 400); |
83 | 144k | return yi < 0 ? yi + 400 : yi; |
84 | 144k | } |
85 | 345k | CONSTEXPR_F int days_per_century(int yi) noexcept { |
86 | 345k | return 36524 + (yi == 0 || yi > 300); |
87 | 345k | } |
88 | 1.91M | CONSTEXPR_F int days_per_4years(int yi) noexcept { |
89 | 1.91M | return 1460 + (yi == 0 || yi > 300 || (yi - 1) % 100 < 96); |
90 | 1.91M | } |
91 | 372k | CONSTEXPR_F int days_per_year(year_t y, month_t m) noexcept { |
92 | 372k | return is_leap_year(y + (m > 2)) ? 366 : 365; |
93 | 372k | } |
94 | | // The compiler cannot optimize away the check if we use |
95 | | // -fsanitize=array-bounds. |
96 | | // m is guaranteed to be in [1:12] in the caller, but the compiler cannot |
97 | | // optimize away the check even when this function is inlined into BreakTime. |
98 | | // To reduce the overhead, we use no_sanitize to skip the unnecessary |
99 | | // -fsanitize=array-bounds check. Remove no_sanitize once the missed |
100 | | // optimization is fixed. |
101 | | #if defined(__clang__) && defined(__has_cpp_attribute) |
102 | | #if __has_cpp_attribute(clang::no_sanitize) |
103 | | [[clang::no_sanitize("array-bounds")]] |
104 | | #endif |
105 | | #endif |
106 | 1.10M | CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept { |
107 | 1.10M | CONSTEXPR_D int k_days_per_month[1 + 12] = { |
108 | 1.10M | -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // non leap year |
109 | 1.10M | }; |
110 | 1.10M | return k_days_per_month[m] + (m == 2 && is_leap_year(y)); |
111 | 1.10M | } |
112 | | |
113 | | CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, |
114 | 339k | hour_t hh, minute_t mm, second_t ss) noexcept { |
115 | 339k | year_t ey = y % 400; |
116 | 339k | const year_t oey = ey; |
117 | 339k | ey += (cd / 146097) * 400; |
118 | 339k | cd %= 146097; |
119 | 339k | if (cd < 0) { |
120 | 21.4k | ey -= 400; |
121 | 21.4k | cd += 146097; |
122 | 21.4k | } |
123 | 339k | ey += (d / 146097) * 400; |
124 | 339k | d = d % 146097 + cd; |
125 | 339k | if (d > 0) { |
126 | 335k | if (d > 146097) { |
127 | 5.31k | ey += 400; |
128 | 5.31k | d -= 146097; |
129 | 5.31k | } |
130 | 335k | } else { |
131 | 4.42k | if (d > -365) { |
132 | | // We often hit the previous year when stepping a civil time backwards, |
133 | | // so special case it to avoid counting up by 100/4/1-year chunks. |
134 | 4.42k | ey -= 1; |
135 | 4.42k | d += days_per_year(ey, m); |
136 | 4.42k | } else { |
137 | 0 | ey -= 400; |
138 | 0 | d += 146097; |
139 | 0 | } |
140 | 4.42k | } |
141 | 339k | if (d > 365) { |
142 | 144k | int yi = year_index(ey, m); // Index into Gregorian 400 year cycle. |
143 | 345k | for (;;) { |
144 | 345k | int n = days_per_century(yi); |
145 | 345k | if (d <= n) break; |
146 | 201k | d -= n; |
147 | 201k | ey += 100; |
148 | 201k | yi += 100; |
149 | 201k | if (yi >= 400) yi -= 400; |
150 | 201k | } |
151 | 1.91M | for (;;) { |
152 | 1.91M | int n = days_per_4years(yi); |
153 | 1.91M | if (d <= n) break; |
154 | 1.76M | d -= n; |
155 | 1.76M | ey += 4; |
156 | 1.76M | yi += 4; |
157 | 1.76M | if (yi >= 400) yi -= 400; |
158 | 1.76M | } |
159 | 368k | for (;;) { |
160 | 368k | int n = days_per_year(ey, m); |
161 | 368k | if (d <= n) break; |
162 | 223k | d -= n; |
163 | 223k | ++ey; |
164 | 223k | } |
165 | 144k | } |
166 | 339k | if (d > 28) { |
167 | 1.10M | for (;;) { |
168 | 1.10M | int n = days_per_month(ey, m); |
169 | 1.10M | if (d <= n) break; |
170 | 895k | d -= n; |
171 | 895k | if (++m > 12) { |
172 | 6.09k | ++ey; |
173 | 6.09k | m = 1; |
174 | 6.09k | } |
175 | 895k | } |
176 | 207k | } |
177 | 339k | return fields(y + (ey - oey), m, static_cast<day_t>(d), hh, mm, ss); |
178 | 339k | } |
179 | | CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, |
180 | 335k | hour_t hh, minute_t mm, second_t ss) noexcept { |
181 | 335k | if (m != 12) { |
182 | 333k | y += m / 12; |
183 | 333k | m %= 12; |
184 | 333k | if (m <= 0) { |
185 | 4.71k | y -= 1; |
186 | 4.71k | m += 12; |
187 | 4.71k | } |
188 | 333k | } |
189 | 335k | return n_day(y, static_cast<month_t>(m), d, cd, hh, mm, ss); |
190 | 335k | } |
191 | | CONSTEXPR_F fields n_hour(year_t y, diff_t m, diff_t d, diff_t cd, |
192 | 316k | diff_t hh, minute_t mm, second_t ss) noexcept { |
193 | 316k | cd += hh / 24; |
194 | 316k | hh %= 24; |
195 | 316k | if (hh < 0) { |
196 | 16.1k | cd -= 1; |
197 | 16.1k | hh += 24; |
198 | 16.1k | } |
199 | 316k | return n_mon(y, m, d, cd, static_cast<hour_t>(hh), mm, ss); |
200 | 316k | } |
201 | | CONSTEXPR_F fields n_min(year_t y, diff_t m, diff_t d, diff_t hh, diff_t ch, |
202 | 316k | diff_t mm, second_t ss) noexcept { |
203 | 316k | ch += mm / 60; |
204 | 316k | mm %= 60; |
205 | 316k | if (mm < 0) { |
206 | 72.5k | ch -= 1; |
207 | 72.5k | mm += 60; |
208 | 72.5k | } |
209 | 316k | return n_hour(y, m, d, hh / 24 + ch / 24, hh % 24 + ch % 24, |
210 | 316k | static_cast<minute_t>(mm), ss); |
211 | 316k | } |
212 | | CONSTEXPR_F fields n_sec(year_t y, diff_t m, diff_t d, diff_t hh, diff_t mm, |
213 | 365k | diff_t ss) noexcept { |
214 | | // Optimization for when (non-constexpr) fields are already normalized. |
215 | 365k | if (0 <= ss && ss < 60) { |
216 | 290k | const second_t nss = static_cast<second_t>(ss); |
217 | 290k | if (0 <= mm && mm < 60) { |
218 | 49.5k | const minute_t nmm = static_cast<minute_t>(mm); |
219 | 49.5k | if (0 <= hh && hh < 24) { |
220 | 49.4k | const hour_t nhh = static_cast<hour_t>(hh); |
221 | 49.4k | if (1 <= d && d <= 28 && 1 <= m && m <= 12) { |
222 | 30.4k | const day_t nd = static_cast<day_t>(d); |
223 | 30.4k | const month_t nm = static_cast<month_t>(m); |
224 | 30.4k | return fields(y, nm, nd, nhh, nmm, nss); |
225 | 30.4k | } |
226 | 19.0k | return n_mon(y, m, d, 0, nhh, nmm, nss); |
227 | 49.4k | } |
228 | 111 | return n_hour(y, m, d, hh / 24, hh % 24, nmm, nss); |
229 | 49.5k | } |
230 | 241k | return n_min(y, m, d, hh, mm / 60, mm % 60, nss); |
231 | 290k | } |
232 | 75.2k | diff_t cm = ss / 60; |
233 | 75.2k | ss %= 60; |
234 | 75.2k | if (ss < 0) { |
235 | 71.5k | cm -= 1; |
236 | 71.5k | ss += 60; |
237 | 71.5k | } |
238 | 75.2k | return n_min(y, m, d, hh, mm / 60 + cm / 60, mm % 60 + cm % 60, |
239 | 75.2k | static_cast<second_t>(ss)); |
240 | 365k | } |
241 | | |
242 | | } // namespace impl |
243 | | |
244 | | //////////////////////////////////////////////////////////////////////// |
245 | | |
246 | | // Increments the indicated (normalized) field by "n". |
247 | 347k | CONSTEXPR_F fields step(second_tag, fields f, diff_t n) noexcept { |
248 | 347k | return impl::n_sec(f.y, f.m, f.d, f.hh, f.mm + n / 60, f.ss + n % 60); |
249 | 347k | } |
250 | 0 | CONSTEXPR_F fields step(minute_tag, fields f, diff_t n) noexcept { |
251 | 0 | return impl::n_min(f.y, f.m, f.d, f.hh + n / 60, 0, f.mm + n % 60, f.ss); |
252 | 0 | } |
253 | 0 | CONSTEXPR_F fields step(hour_tag, fields f, diff_t n) noexcept { |
254 | 0 | return impl::n_hour(f.y, f.m, f.d + n / 24, 0, f.hh + n % 24, f.mm, f.ss); |
255 | 0 | } |
256 | 4.30k | CONSTEXPR_F fields step(day_tag, fields f, diff_t n) noexcept { |
257 | 4.30k | return impl::n_day(f.y, f.m, f.d, n, f.hh, f.mm, f.ss); |
258 | 4.30k | } |
259 | 0 | CONSTEXPR_F fields step(month_tag, fields f, diff_t n) noexcept { |
260 | 0 | return impl::n_mon(f.y + n / 12, f.m + n % 12, f.d, 0, f.hh, f.mm, f.ss); |
261 | 0 | } |
262 | 0 | CONSTEXPR_F fields step(year_tag, fields f, diff_t n) noexcept { |
263 | 0 | return fields(f.y + n, f.m, f.d, f.hh, f.mm, f.ss); |
264 | 0 | } |
265 | | |
266 | | //////////////////////////////////////////////////////////////////////// |
267 | | |
268 | | namespace impl { |
269 | | |
270 | | // Returns (v * f + a) but avoiding intermediate overflow when possible. |
271 | 27.3k | CONSTEXPR_F diff_t scale_add(diff_t v, diff_t f, diff_t a) noexcept { |
272 | 27.3k | return (v < 0) ? ((v + 1) * f + a) - f : ((v - 1) * f + a) + f; |
273 | 27.3k | } |
274 | | |
275 | | // Map a (normalized) Y/M/D to the number of days before/after 1970-01-01. |
276 | | // Probably overflows for years outside [-292277022656:292277026595]. |
277 | 24.5k | CONSTEXPR_F diff_t ymd_ord(year_t y, month_t m, day_t d) noexcept { |
278 | 24.5k | const diff_t eyear = (m <= 2) ? y - 1 : y; |
279 | 24.5k | const diff_t era = (eyear >= 0 ? eyear : eyear - 399) / 400; |
280 | 24.5k | const diff_t yoe = eyear - era * 400; |
281 | 24.5k | const diff_t doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; |
282 | 24.5k | const diff_t doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; |
283 | 24.5k | return era * 146097 + doe - 719468; |
284 | 24.5k | } |
285 | | |
286 | | // Returns the difference in days between two normalized Y-M-D tuples. |
287 | | // ymd_ord() will encounter integer overflow given extreme year values, |
288 | | // yet the difference between two such extreme values may actually be |
289 | | // small, so we take a little care to avoid overflow when possible by |
290 | | // exploiting the 146097-day cycle. |
291 | | CONSTEXPR_F diff_t day_difference(year_t y1, month_t m1, day_t d1, |
292 | 12.2k | year_t y2, month_t m2, day_t d2) noexcept { |
293 | 12.2k | const diff_t a_c4_off = y1 % 400; |
294 | 12.2k | const diff_t b_c4_off = y2 % 400; |
295 | 12.2k | diff_t c4_diff = (y1 - a_c4_off) - (y2 - b_c4_off); |
296 | 12.2k | diff_t delta = ymd_ord(a_c4_off, m1, d1) - ymd_ord(b_c4_off, m2, d2); |
297 | 12.2k | if (c4_diff > 0 && delta < 0) { |
298 | 333 | delta += 2 * 146097; |
299 | 333 | c4_diff -= 2 * 400; |
300 | 11.9k | } else if (c4_diff < 0 && delta > 0) { |
301 | 15 | delta -= 2 * 146097; |
302 | 15 | c4_diff += 2 * 400; |
303 | 15 | } |
304 | 12.2k | return (c4_diff / 400 * 146097) + delta; |
305 | 12.2k | } |
306 | | |
307 | | } // namespace impl |
308 | | |
309 | | // Returns the difference between fields structs using the indicated unit. |
310 | 0 | CONSTEXPR_F diff_t difference(year_tag, fields f1, fields f2) noexcept { |
311 | 0 | return f1.y - f2.y; |
312 | 0 | } |
313 | 0 | CONSTEXPR_F diff_t difference(month_tag, fields f1, fields f2) noexcept { |
314 | 0 | return impl::scale_add(difference(year_tag{}, f1, f2), 12, (f1.m - f2.m)); |
315 | 0 | } |
316 | 12.2k | CONSTEXPR_F diff_t difference(day_tag, fields f1, fields f2) noexcept { |
317 | 12.2k | return impl::day_difference(f1.y, f1.m, f1.d, f2.y, f2.m, f2.d); |
318 | 12.2k | } |
319 | 9.11k | CONSTEXPR_F diff_t difference(hour_tag, fields f1, fields f2) noexcept { |
320 | 9.11k | return impl::scale_add(difference(day_tag{}, f1, f2), 24, (f1.hh - f2.hh)); |
321 | 9.11k | } |
322 | 9.11k | CONSTEXPR_F diff_t difference(minute_tag, fields f1, fields f2) noexcept { |
323 | 9.11k | return impl::scale_add(difference(hour_tag{}, f1, f2), 60, (f1.mm - f2.mm)); |
324 | 9.11k | } |
325 | 9.11k | CONSTEXPR_F diff_t difference(second_tag, fields f1, fields f2) noexcept { |
326 | 9.11k | return impl::scale_add(difference(minute_tag{}, f1, f2), 60, f1.ss - f2.ss); |
327 | 9.11k | } |
328 | | |
329 | | //////////////////////////////////////////////////////////////////////// |
330 | | |
331 | | // Aligns the (normalized) fields struct to the indicated field. |
332 | 366k | CONSTEXPR_F fields align(second_tag, fields f) noexcept { |
333 | 366k | return f; |
334 | 366k | } |
335 | 0 | CONSTEXPR_F fields align(minute_tag, fields f) noexcept { |
336 | 0 | return fields{f.y, f.m, f.d, f.hh, f.mm, 0}; |
337 | 0 | } |
338 | 0 | CONSTEXPR_F fields align(hour_tag, fields f) noexcept { |
339 | 0 | return fields{f.y, f.m, f.d, f.hh, 0, 0}; |
340 | 0 | } |
341 | 14.1k | CONSTEXPR_F fields align(day_tag, fields f) noexcept { |
342 | 14.1k | return fields{f.y, f.m, f.d, 0, 0, 0}; |
343 | 14.1k | } |
344 | 0 | CONSTEXPR_F fields align(month_tag, fields f) noexcept { |
345 | 0 | return fields{f.y, f.m, 1, 0, 0, 0}; |
346 | 0 | } |
347 | 3.45k | CONSTEXPR_F fields align(year_tag, fields f) noexcept { |
348 | 3.45k | return fields{f.y, 1, 1, 0, 0, 0}; |
349 | 3.45k | } |
350 | | |
351 | | //////////////////////////////////////////////////////////////////////// |
352 | | |
353 | | template <typename T> |
354 | | class civil_time { |
355 | | public: |
356 | | explicit CONSTEXPR_M civil_time(year_t y, diff_t m = 1, diff_t d = 1, |
357 | | diff_t hh = 0, diff_t mm = 0, |
358 | | diff_t ss = 0) noexcept |
359 | 18.1k | : civil_time(impl::n_sec(y, m, d, hh, mm, ss)) {} cctz::detail::civil_time<cctz::detail::second_tag>::civil_time(long, long, long, long, long, long) Line | Count | Source | 359 | 14.6k | : civil_time(impl::n_sec(y, m, d, hh, mm, ss)) {} |
cctz::detail::civil_time<cctz::detail::day_tag>::civil_time(long, long, long, long, long, long) Line | Count | Source | 359 | 3.17k | : civil_time(impl::n_sec(y, m, d, hh, mm, ss)) {} |
cctz::detail::civil_time<cctz::detail::year_tag>::civil_time(long, long, long, long, long, long) Line | Count | Source | 359 | 282 | : civil_time(impl::n_sec(y, m, d, hh, mm, ss)) {} |
|
360 | | |
361 | 177k | CONSTEXPR_M civil_time() noexcept : f_{1970, 1, 1, 0, 0, 0} {} |
362 | | civil_time(const civil_time&) = default; |
363 | | civil_time& operator=(const civil_time&) = default; |
364 | | |
365 | | // Conversion between civil times of different alignment. Conversion to |
366 | | // a more precise alignment is allowed implicitly (e.g., day -> hour), |
367 | | // but conversion where information is discarded must be explicit |
368 | | // (e.g., second -> minute). |
369 | | template <typename U, typename S> |
370 | | using preserves_data = |
371 | | typename std::enable_if<std::is_base_of<U, S>::value>::type; |
372 | | template <typename U> |
373 | | CONSTEXPR_M civil_time(const civil_time<U>& ct, |
374 | | preserves_data<T, U>* = nullptr) noexcept |
375 | 7.19k | : civil_time(ct.f_) {} _ZN4cctz6detail10civil_timeINS0_10second_tagEEC2INS0_7day_tagEEERKNS1_IT_EEPNSt3__19enable_ifIXsr3std10is_base_ofIS2_S6_EE5valueEvE4typeE Line | Count | Source | 375 | 3.73k | : civil_time(ct.f_) {} |
_ZN4cctz6detail10civil_timeINS0_7day_tagEEC2INS0_8year_tagEEERKNS1_IT_EEPNSt3__19enable_ifIXsr3std10is_base_ofIS2_S6_EE5valueEvE4typeE Line | Count | Source | 375 | 3.45k | : civil_time(ct.f_) {} |
|
376 | | template <typename U> |
377 | | explicit CONSTEXPR_M civil_time(const civil_time<U>& ct, |
378 | | preserves_data<U, T>* = nullptr) noexcept |
379 | 6.35k | : civil_time(ct.f_) {} _ZN4cctz6detail10civil_timeINS0_8year_tagEEC2INS0_7day_tagEEERKNS1_IT_EEPNSt3__19enable_ifIXsr3std10is_base_ofIS6_S2_EE5valueEvE4typeE Line | Count | Source | 379 | 3.17k | : civil_time(ct.f_) {} |
_ZN4cctz6detail10civil_timeINS0_7day_tagEEC2INS0_10second_tagEEERKNS1_IT_EEPNSt3__19enable_ifIXsr3std10is_base_ofIS6_S2_EE5valueEvE4typeE Line | Count | Source | 379 | 3.17k | : civil_time(ct.f_) {} |
|
380 | | |
381 | | // Factories for the maximum/minimum representable civil_time. |
382 | 411 | static CONSTEXPR_F auto (max)() -> civil_time { |
383 | 411 | const auto max_year = (std::numeric_limits<std::int_least64_t>::max)(); |
384 | 411 | return civil_time(max_year, 12, 31, 23, 59, 59); |
385 | 411 | } |
386 | 548 | static CONSTEXPR_F auto (min)() -> civil_time { |
387 | 548 | const auto min_year = (std::numeric_limits<std::int_least64_t>::min)(); |
388 | 548 | return civil_time(min_year, 1, 1, 0, 0, 0); |
389 | 548 | } |
390 | | |
391 | | // Field accessors. Note: All but year() return an int. |
392 | 489k | CONSTEXPR_M year_t year() const noexcept { return f_.y; } cctz::detail::civil_time<cctz::detail::second_tag>::year() const Line | Count | Source | 392 | 485k | CONSTEXPR_M year_t year() const noexcept { return f_.y; } |
cctz::detail::civil_time<cctz::detail::day_tag>::year() const Line | Count | Source | 392 | 3.45k | CONSTEXPR_M year_t year() const noexcept { return f_.y; } |
cctz::detail::civil_time<cctz::detail::year_tag>::year() const Line | Count | Source | 392 | 282 | CONSTEXPR_M year_t year() const noexcept { return f_.y; } |
|
393 | 135k | CONSTEXPR_M int month() const noexcept { return f_.m; } cctz::detail::civil_time<cctz::detail::second_tag>::month() const Line | Count | Source | 393 | 132k | CONSTEXPR_M int month() const noexcept { return f_.m; } |
cctz::detail::civil_time<cctz::detail::day_tag>::month() const Line | Count | Source | 393 | 3.45k | CONSTEXPR_M int month() const noexcept { return f_.m; } |
|
394 | 40.5k | CONSTEXPR_M int day() const noexcept { return f_.d; } cctz::detail::civil_time<cctz::detail::second_tag>::day() const Line | Count | Source | 394 | 37.0k | CONSTEXPR_M int day() const noexcept { return f_.d; } |
cctz::detail::civil_time<cctz::detail::day_tag>::day() const Line | Count | Source | 394 | 3.45k | CONSTEXPR_M int day() const noexcept { return f_.d; } |
|
395 | 13.0k | CONSTEXPR_M int hour() const noexcept { return f_.hh; } |
396 | 13.4k | CONSTEXPR_M int minute() const noexcept { return f_.mm; } |
397 | 14.2k | CONSTEXPR_M int second() const noexcept { return f_.ss; } |
398 | | |
399 | | // Assigning arithmetic. |
400 | | CONSTEXPR_M civil_time& operator+=(diff_t n) noexcept { |
401 | | return *this = *this + n; |
402 | | } |
403 | 3.82k | CONSTEXPR_M civil_time& operator-=(diff_t n) noexcept { |
404 | 3.82k | return *this = *this - n; |
405 | 3.82k | } |
406 | | CONSTEXPR_M civil_time& operator++() noexcept { |
407 | | return *this += 1; |
408 | | } |
409 | | CONSTEXPR_M civil_time operator++(int) noexcept { |
410 | | const civil_time a = *this; |
411 | | ++*this; |
412 | | return a; |
413 | | } |
414 | | CONSTEXPR_M civil_time& operator--() noexcept { |
415 | | return *this -= 1; |
416 | | } |
417 | | CONSTEXPR_M civil_time operator--(int) noexcept { |
418 | | const civil_time a = *this; |
419 | | --*this; |
420 | | return a; |
421 | | } |
422 | | |
423 | | // Binary arithmetic operators. |
424 | 278k | friend CONSTEXPR_F civil_time operator+(civil_time a, diff_t n) noexcept { |
425 | 278k | return civil_time(step(T{}, a.f_, n)); |
426 | 278k | } cctz::detail::operator+(cctz::detail::civil_time<cctz::detail::day_tag>, long) Line | Count | Source | 424 | 564 | friend CONSTEXPR_F civil_time operator+(civil_time a, diff_t n) noexcept { | 425 | 564 | return civil_time(step(T{}, a.f_, n)); | 426 | 564 | } |
cctz::detail::operator+(cctz::detail::civil_time<cctz::detail::second_tag>, long) Line | Count | Source | 424 | 277k | friend CONSTEXPR_F civil_time operator+(civil_time a, diff_t n) noexcept { | 425 | 277k | return civil_time(step(T{}, a.f_, n)); | 426 | 277k | } |
|
427 | | friend CONSTEXPR_F civil_time operator+(diff_t n, civil_time a) noexcept { |
428 | | return a + n; |
429 | | } |
430 | 73.9k | friend CONSTEXPR_F civil_time operator-(civil_time a, diff_t n) noexcept { |
431 | 73.9k | return n != (std::numeric_limits<diff_t>::min)() |
432 | 73.9k | ? civil_time(step(T{}, a.f_, -n)) |
433 | 73.9k | : civil_time(step(T{}, step(T{}, a.f_, -(n + 1)), 1)); |
434 | 73.9k | } cctz::detail::operator-(cctz::detail::civil_time<cctz::detail::day_tag>, long) Line | Count | Source | 430 | 3.73k | friend CONSTEXPR_F civil_time operator-(civil_time a, diff_t n) noexcept { | 431 | 3.73k | return n != (std::numeric_limits<diff_t>::min)() | 432 | 3.73k | ? civil_time(step(T{}, a.f_, -n)) | 433 | 3.73k | : civil_time(step(T{}, step(T{}, a.f_, -(n + 1)), 1)); | 434 | 3.73k | } |
cctz::detail::operator-(cctz::detail::civil_time<cctz::detail::second_tag>, long) Line | Count | Source | 430 | 70.2k | friend CONSTEXPR_F civil_time operator-(civil_time a, diff_t n) noexcept { | 431 | 70.2k | return n != (std::numeric_limits<diff_t>::min)() | 432 | 70.2k | ? civil_time(step(T{}, a.f_, -n)) | 433 | 70.2k | : civil_time(step(T{}, step(T{}, a.f_, -(n + 1)), 1)); | 434 | 70.2k | } |
|
435 | 12.2k | friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept { |
436 | 12.2k | return difference(T{}, lhs.f_, rhs.f_); |
437 | 12.2k | } cctz::detail::operator-(cctz::detail::civil_time<cctz::detail::day_tag>, cctz::detail::civil_time<cctz::detail::day_tag>) Line | Count | Source | 435 | 3.17k | friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept { | 436 | 3.17k | return difference(T{}, lhs.f_, rhs.f_); | 437 | 3.17k | } |
cctz::detail::operator-(cctz::detail::civil_time<cctz::detail::second_tag>, cctz::detail::civil_time<cctz::detail::second_tag>) Line | Count | Source | 435 | 9.11k | friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept { | 436 | 9.11k | return difference(T{}, lhs.f_, rhs.f_); | 437 | 9.11k | } |
|
438 | | |
439 | | private: |
440 | | // All instantiations of this template are allowed to call the following |
441 | | // private constructor and access the private fields member. |
442 | | template <typename U> |
443 | | friend class civil_time; |
444 | | |
445 | | // The designated constructor that all others eventually call. |
446 | 383k | explicit CONSTEXPR_M civil_time(fields f) noexcept : f_(align(T{}, f)) {} cctz::detail::civil_time<cctz::detail::second_tag>::civil_time(cctz::detail::fields) Line | Count | Source | 446 | 366k | explicit CONSTEXPR_M civil_time(fields f) noexcept : f_(align(T{}, f)) {} |
cctz::detail::civil_time<cctz::detail::day_tag>::civil_time(cctz::detail::fields) Line | Count | Source | 446 | 14.1k | explicit CONSTEXPR_M civil_time(fields f) noexcept : f_(align(T{}, f)) {} |
cctz::detail::civil_time<cctz::detail::year_tag>::civil_time(cctz::detail::fields) Line | Count | Source | 446 | 3.45k | explicit CONSTEXPR_M civil_time(fields f) noexcept : f_(align(T{}, f)) {} |
|
447 | | |
448 | | fields f_; |
449 | | }; |
450 | | |
451 | | // Disallows difference between differently aligned types. |
452 | | // auto n = civil_day(...) - civil_hour(...); // would be confusing. |
453 | | template <typename T, typename U> |
454 | | CONSTEXPR_F diff_t operator-(civil_time<T>, civil_time<U>) = delete; |
455 | | |
456 | | using civil_year = civil_time<year_tag>; |
457 | | using civil_month = civil_time<month_tag>; |
458 | | using civil_day = civil_time<day_tag>; |
459 | | using civil_hour = civil_time<hour_tag>; |
460 | | using civil_minute = civil_time<minute_tag>; |
461 | | using civil_second = civil_time<second_tag>; |
462 | | |
463 | | //////////////////////////////////////////////////////////////////////// |
464 | | |
465 | | // Relational operators that work with differently aligned objects. |
466 | | // Always compares all six fields. |
467 | | template <typename T1, typename T2> |
468 | | CONSTEXPR_F bool operator<(const civil_time<T1>& lhs, |
469 | 140k | const civil_time<T2>& rhs) noexcept { |
470 | 140k | return (lhs.year() < rhs.year() || |
471 | 140k | (lhs.year() == rhs.year() && |
472 | 76.2k | (lhs.month() < rhs.month() || |
473 | 38.2k | (lhs.month() == rhs.month() && |
474 | 3.55k | (lhs.day() < rhs.day() || |
475 | 1.16k | (lhs.day() == rhs.day() && |
476 | 872 | (lhs.hour() < rhs.hour() || |
477 | 573 | (lhs.hour() == rhs.hour() && |
478 | 497 | (lhs.minute() < rhs.minute() || |
479 | 408 | (lhs.minute() == rhs.minute() && |
480 | 359 | (lhs.second() < rhs.second()))))))))))); |
481 | 140k | } |
482 | | template <typename T1, typename T2> |
483 | | CONSTEXPR_F bool operator<=(const civil_time<T1>& lhs, |
484 | 8.71k | const civil_time<T2>& rhs) noexcept { |
485 | 8.71k | return !(rhs < lhs); |
486 | 8.71k | } |
487 | | template <typename T1, typename T2> |
488 | | CONSTEXPR_F bool operator>=(const civil_time<T1>& lhs, |
489 | 9.97k | const civil_time<T2>& rhs) noexcept { |
490 | 9.97k | return !(lhs < rhs); |
491 | 9.97k | } |
492 | | template <typename T1, typename T2> |
493 | | CONSTEXPR_F bool operator>(const civil_time<T1>& lhs, |
494 | 3.65k | const civil_time<T2>& rhs) noexcept { |
495 | 3.65k | return rhs < lhs; |
496 | 3.65k | } |
497 | | template <typename T1, typename T2> |
498 | | CONSTEXPR_F bool operator==(const civil_time<T1>& lhs, |
499 | | const civil_time<T2>& rhs) noexcept { |
500 | | return lhs.year() == rhs.year() && lhs.month() == rhs.month() && |
501 | | lhs.day() == rhs.day() && lhs.hour() == rhs.hour() && |
502 | | lhs.minute() == rhs.minute() && lhs.second() == rhs.second(); |
503 | | } |
504 | | template <typename T1, typename T2> |
505 | | CONSTEXPR_F bool operator!=(const civil_time<T1>& lhs, |
506 | | const civil_time<T2>& rhs) noexcept { |
507 | | return !(lhs == rhs); |
508 | | } |
509 | | |
510 | | //////////////////////////////////////////////////////////////////////// |
511 | | |
512 | | enum class weekday { |
513 | | monday, |
514 | | tuesday, |
515 | | wednesday, |
516 | | thursday, |
517 | | friday, |
518 | | saturday, |
519 | | sunday, |
520 | | }; |
521 | | |
522 | 9.95k | CONSTEXPR_F weekday get_weekday(const civil_second& cs) noexcept { |
523 | 9.95k | CONSTEXPR_D weekday k_weekday_by_mon_off[13] = { |
524 | 9.95k | weekday::monday, weekday::tuesday, weekday::wednesday, |
525 | 9.95k | weekday::thursday, weekday::friday, weekday::saturday, |
526 | 9.95k | weekday::sunday, weekday::monday, weekday::tuesday, |
527 | 9.95k | weekday::wednesday, weekday::thursday, weekday::friday, |
528 | 9.95k | weekday::saturday, |
529 | 9.95k | }; |
530 | 9.95k | CONSTEXPR_D int k_weekday_offsets[1 + 12] = { |
531 | 9.95k | -1, 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4, |
532 | 9.95k | }; |
533 | 9.95k | year_t wd = 2400 + (cs.year() % 400) - (cs.month() < 3); |
534 | 9.95k | wd += wd / 4 - wd / 100 + wd / 400; |
535 | 9.95k | wd += k_weekday_offsets[cs.month()] + cs.day(); |
536 | 9.95k | return k_weekday_by_mon_off[wd % 7 + 6]; |
537 | 9.95k | } |
538 | | |
539 | | //////////////////////////////////////////////////////////////////////// |
540 | | |
541 | 282 | CONSTEXPR_F civil_day next_weekday(civil_day cd, weekday wd) noexcept { |
542 | 282 | CONSTEXPR_D weekday k_weekdays_forw[14] = { |
543 | 282 | weekday::monday, weekday::tuesday, weekday::wednesday, |
544 | 282 | weekday::thursday, weekday::friday, weekday::saturday, |
545 | 282 | weekday::sunday, weekday::monday, weekday::tuesday, |
546 | 282 | weekday::wednesday, weekday::thursday, weekday::friday, |
547 | 282 | weekday::saturday, weekday::sunday, |
548 | 282 | }; |
549 | 282 | weekday base = get_weekday(cd); |
550 | 1.79k | for (int i = 0;; ++i) { |
551 | 1.79k | if (base == k_weekdays_forw[i]) { |
552 | 1.29k | for (int j = i + 1;; ++j) { |
553 | 1.29k | if (wd == k_weekdays_forw[j]) { |
554 | 282 | return cd + (j - i); |
555 | 282 | } |
556 | 1.29k | } |
557 | 282 | } |
558 | 1.79k | } |
559 | 282 | } |
560 | | |
561 | 3.45k | CONSTEXPR_F civil_day prev_weekday(civil_day cd, weekday wd) noexcept { |
562 | 3.45k | CONSTEXPR_D weekday k_weekdays_back[14] = { |
563 | 3.45k | weekday::sunday, weekday::saturday, weekday::friday, |
564 | 3.45k | weekday::thursday, weekday::wednesday, weekday::tuesday, |
565 | 3.45k | weekday::monday, weekday::sunday, weekday::saturday, |
566 | 3.45k | weekday::friday, weekday::thursday, weekday::wednesday, |
567 | 3.45k | weekday::tuesday, weekday::monday, |
568 | 3.45k | }; |
569 | 3.45k | weekday base = get_weekday(cd); |
570 | 12.7k | for (int i = 0;; ++i) { |
571 | 12.7k | if (base == k_weekdays_back[i]) { |
572 | 15.4k | for (int j = i + 1;; ++j) { |
573 | 15.4k | if (wd == k_weekdays_back[j]) { |
574 | 3.45k | return cd - (j - i); |
575 | 3.45k | } |
576 | 15.4k | } |
577 | 3.45k | } |
578 | 12.7k | } |
579 | 3.45k | } |
580 | | |
581 | 6.14k | CONSTEXPR_F int get_yearday(const civil_second& cs) noexcept { |
582 | 6.14k | CONSTEXPR_D int k_month_offsets[1 + 12] = { |
583 | 6.14k | -1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, |
584 | 6.14k | }; |
585 | 6.14k | const int feb29 = (cs.month() > 2 && impl::is_leap_year(cs.year())); |
586 | 6.14k | return k_month_offsets[cs.month()] + feb29 + cs.day(); |
587 | 6.14k | } |
588 | | |
589 | | //////////////////////////////////////////////////////////////////////// |
590 | | |
591 | | std::ostream& operator<<(std::ostream& os, const civil_year& y); |
592 | | std::ostream& operator<<(std::ostream& os, const civil_month& m); |
593 | | std::ostream& operator<<(std::ostream& os, const civil_day& d); |
594 | | std::ostream& operator<<(std::ostream& os, const civil_hour& h); |
595 | | std::ostream& operator<<(std::ostream& os, const civil_minute& m); |
596 | | std::ostream& operator<<(std::ostream& os, const civil_second& s); |
597 | | std::ostream& operator<<(std::ostream& os, weekday wd); |
598 | | |
599 | | } // namespace detail |
600 | | } // namespace cctz |
601 | | |
602 | | #undef CONSTEXPR_M |
603 | | #undef CONSTEXPR_F |
604 | | #undef CONSTEXPR_D |
605 | | |
606 | | #endif // CCTZ_CIVIL_TIME_DETAIL_H_ |