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
34namespace absl {
35namespace time_internal {
36namespace cctz {
37
38// Support years that at least span the range of 64-bit time_t values.
39using 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).
43using diff_t = std::int_fast64_t;
44
45namespace detail {
46
47// Type aliases that indicate normalized argument values.
48using month_t = std::int_fast8_t; // [1:12]
49using day_t = std::int_fast8_t; // [1:31]
50using hour_t = std::int_fast8_t; // [0:23]
51using minute_t = std::int_fast8_t; // [0:59]
52using second_t = std::int_fast8_t; // [0:59]
53
54// Normalized civil-time fields: Y-M-D HH:MM:SS.
55struct 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
67struct second_tag {};
68struct minute_tag : second_tag {};
69struct hour_tag : minute_tag {};
70struct day_tag : hour_tag {};
71struct month_tag : day_tag {};
72struct year_tag : month_tag {};
73
74////////////////////////////////////////////////////////////////////////
75
76// Field normalization (without avoidable overflow).
77
78namespace impl {
79
80CONSTEXPR_F bool is_leap_year(year_t y) noexcept {
81 return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
82}
83CONSTEXPR_F int year_index(year_t y, month_t m) noexcept {
84 return (static_cast<int>((y + (m > 2)) % 400) + 400) % 400;
85}
86CONSTEXPR_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}
90CONSTEXPR_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}
94CONSTEXPR_F int days_per_year(year_t y, month_t m) noexcept {
95 return is_leap_year(y + (m > 2)) ? 366 : 365;
96}
97CONSTEXPR_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
104CONSTEXPR_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}
155CONSTEXPR_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}
167CONSTEXPR_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}
177CONSTEXPR_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}
188CONSTEXPR_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".
223CONSTEXPR_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}
226CONSTEXPR_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}
229CONSTEXPR_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}
232CONSTEXPR_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}
235CONSTEXPR_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}
238CONSTEXPR_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
244namespace impl {
245
246// Returns (v * f + a) but avoiding intermediate overflow when possible.
247CONSTEXPR_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].
253CONSTEXPR_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.
267CONSTEXPR_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.
286CONSTEXPR_F diff_t difference(year_tag, fields f1, fields f2) noexcept {
287 return f1.y - f2.y;
288}
289CONSTEXPR_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}
292CONSTEXPR_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}
295CONSTEXPR_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}
298CONSTEXPR_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}
301CONSTEXPR_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.
308CONSTEXPR_F fields align(second_tag, fields f) noexcept {
309 return f;
310}
311CONSTEXPR_F fields align(minute_tag, fields f) noexcept {
312 return fields{f.y, f.m, f.d, f.hh, f.mm, 0};
313}
314CONSTEXPR_F fields align(hour_tag, fields f) noexcept {
315 return fields{f.y, f.m, f.d, f.hh, 0, 0};
316}
317CONSTEXPR_F fields align(day_tag, fields f) noexcept {
318 return fields{f.y, f.m, f.d, 0, 0, 0};
319}
320CONSTEXPR_F fields align(month_tag, fields f) noexcept {
321 return fields{f.y, f.m, 1, 0, 0, 0};
322}
323CONSTEXPR_F fields align(year_tag, fields f) noexcept {
324 return fields{f.y, 1, 1, 0, 0, 0};
325}
326
327////////////////////////////////////////////////////////////////////////
328
329namespace impl {
330
331template <typename H>
332H 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}
335template <typename H>
336H 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}
339template <typename H>
340H AbslHashValueImpl(hour_tag, H h, fields f) {
341 return H::combine(std::move(h), f.y, f.m, f.d, f.hh);
342}
343template <typename H>
344H AbslHashValueImpl(day_tag, H h, fields f) {
345 return H::combine(std::move(h), f.y, f.m, f.d);
346}
347template <typename H>
348H AbslHashValueImpl(month_tag, H h, fields f) {
349 return H::combine(std::move(h), f.y, f.m);
350}
351template <typename H>
352H AbslHashValueImpl(year_tag, H h, fields f) {
353 return H::combine(std::move(h), f.y);
354}
355
356} // namespace impl
357
358////////////////////////////////////////////////////////////////////////
359
360template <typename T>
361class 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.
469template <typename T, typename U>
470CONSTEXPR_F diff_t operator-(civil_time<T>, civil_time<U>) = delete;
471
472using civil_year = civil_time<year_tag>;
473using civil_month = civil_time<month_tag>;
474using civil_day = civil_time<day_tag>;
475using civil_hour = civil_time<hour_tag>;
476using civil_minute = civil_time<minute_tag>;
477using civil_second = civil_time<second_tag>;
478
479////////////////////////////////////////////////////////////////////////
480
481// Relational operators that work with differently aligned objects.
482// Always compares all six fields.
483template <typename T1, typename T2>
484CONSTEXPR_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}
498template <typename T1, typename T2>
499CONSTEXPR_F bool operator<=(const civil_time<T1>& lhs,
500 const civil_time<T2>& rhs) noexcept {
501 return !(rhs < lhs);
502}
503template <typename T1, typename T2>
504CONSTEXPR_F bool operator>=(const civil_time<T1>& lhs,
505 const civil_time<T2>& rhs) noexcept {
506 return !(lhs < rhs);
507}
508template <typename T1, typename T2>
509CONSTEXPR_F bool operator>(const civil_time<T1>& lhs,
510 const civil_time<T2>& rhs) noexcept {
511 return rhs < lhs;
512}
513template <typename T1, typename T2>
514CONSTEXPR_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}
520template <typename T1, typename T2>
521CONSTEXPR_F bool operator!=(const civil_time<T1>& lhs,
522 const civil_time<T2>& rhs) noexcept {
523 return !(lhs == rhs);
524}
525
526////////////////////////////////////////////////////////////////////////
527
528enum class weekday {
529 monday,
530 tuesday,
531 wednesday,
532 thursday,
533 friday,
534 saturday,
535 sunday,
536};
537
538template <typename T>
539CONSTEXPR_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
558CONSTEXPR_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
578CONSTEXPR_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
598template <typename T>
599CONSTEXPR_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
609std::ostream& operator<<(std::ostream& os, const civil_year& y);
610std::ostream& operator<<(std::ostream& os, const civil_month& m);
611std::ostream& operator<<(std::ostream& os, const civil_day& d);
612std::ostream& operator<<(std::ostream& os, const civil_hour& h);
613std::ostream& operator<<(std::ostream& os, const civil_minute& m);
614std::ostream& operator<<(std::ostream& os, const civil_second& s);
615std::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