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