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