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