/* * Copyright (c) 2026 Dylan Araps * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef DYLAN_DATE_H #define DYLAN_DATE_H // // Fast date algorithm, C implementation. // Source: https://www.benjoffe.com/fast-date-64 // #include "util.h" #define C1 505054698555331ull // floor(2^64 * 4 / 146097) #define C2 50504432782230121ull // ceil(2^64 * 4 / 1461) #define C3 8619973866219416ull // floor(2^64 / 2140) #define SCALE 32u #define SHIFT0 (30556u * SCALE) #define SHIFT1 (5980u * SCALE) #if defined(__SIZEOF_INT128__) #define ERAS 4726498270ull #define D_SHIFT ((u64)(146097ull * ERAS - 719469ull)) #define Y_SHIFT ((u64)(400ull * ERAS - 1ull)) static inline u64 mulhi(u64 a, u64 b) { __uint128_t p = (__uint128_t)a * (__uint128_t)b; return (u64)(p >> 64); } static inline void mul128(u64 a, u64 b, u64 *hi, u64 *lo) { __uint128_t p = (__uint128_t)a * (__uint128_t)b; *hi = (u64)(p >> 64); *lo = (u64)p; } #else #define ERAS 14704u #define D_SHIFT ((u64)(146097ull * (u64)ERAS - 719469ull)) #define Y_SHIFT ((u64)(400ull * (u64)ERAS - 1ull)) static inline u64 mulhi(u64 a, u64 b) { u64 a0 = (u32)a; u64 a1 = a >> 32; u64 b0 = (u32)b; u64 b1 = b >> 32; u64 p00 = a0 * b0; u64 p01 = a0 * b1; u64 p10 = a1 * b0; u64 p11 = a1 * b1; u64 mid = (p00 >> 32) + (u32)p01 + (u32)p10; return p11 + (p01 >> 32) + (p10 >> 32) + (mid >> 32); } static inline void mul128(u64 a, u64 b, u64 *hi, u64 *lo) { u64 a0 = (u32)a; u64 a1 = a >> 32; u64 b0 = (u32)b; u64 b1 = b >> 32; u64 p00 = a0 * b0; u64 p01 = a0 * b1; u64 p10 = a1 * b0; u64 p11 = a1 * b1; u64 mid = (p00 >> 32) + (u32)p01 + (u32)p10; *hi = p11 + (p01 >> 32) + (p10 >> 32) + (mid >> 32); *lo = (mid << 32) | (u32)p00; } #endif // defined(__SIZEOF_INT128__) static inline void ut_to_date(s32 day, s32 *Y, u32 *M, u32 *D) { u64 rev = (u64)D_SHIFT - (u64)(s64)day; // Reverse day count. u64 cen = mulhi(C1, rev); // Divide 36524.25 u64 jul = rev - cen / 4u + cen; // Julian map. u64 num_hi, num_lo; mul128(C2, jul, &num_hi, &num_lo); // Divide 365.25 u64 yrs64 = (u64)Y_SHIFT - (u64)num_hi; // Forward year. u64 yrs = (u32)yrs64; u32 ypt = (u32)mulhi((u64)(24451u * SCALE), num_lo); // Year (backwards). u32 bump = (ypt < (3952u * SCALE)); // Jan or Feb. u32 shift = bump ? SHIFT1 : SHIFT0; // Month offset. u32 N = (u32)((yrs % 4u) * (16u * SCALE) + shift - ypt); // Leap years. u32 m = N / (2048u * SCALE); u32 d = (u32)mulhi(C3, (u64)(N % (2048u * SCALE))); // Divide 2140 *Y = (s32)yrs + (s32)bump; *M = m; *D = d + 1u; } static inline void ut_to_date_time(s64 tz, s32 day, s32 *Y, u32 *M, u32 *D, u32 *h, u32 *m, u32 *s) { s64 us = tz + day; s32 days = (s32)(us / 86400); s32 r = (s32)(us % 86400); if (r < 0) { r += 86400; days -= 1; } ut_to_date(days, Y, M, D); u32 hr = r / 3600; r -= hr * 3600; *h = hr; u32 mn = r / 60; *m = mn; *s = r - mn * 60; } #endif // DYLAN_DATE_H