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