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