Coverage Report

Created: 2025-08-26 06:36

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