77#include "ruby/util.h"
88#include <math.h>
99#include <time.h>
10- #include <stdint.h> /* For uint64_t in Neri-Schneider algorithm */
1110#if defined(HAVE_SYS_TIME_H )
1211#include <sys/time.h>
1312#endif
@@ -453,6 +452,9 @@ do {\
453452static int c_valid_civil_p (int , int , int , double ,
454453 int * , int * , int * , int * );
455454
455+ /* Check if using pure Gregorian calendar (sg == -Infinity) */
456+ #define c_gregorian_only_p (sg ) (isinf(sg) && (sg) < 0)
457+
456458/* Forward declarations for Neri-Schneider optimized functions */
457459static int c_gregorian_civil_to_jd (int y , int m , int d );
458460static void c_gregorian_jd_to_civil (int jd , int * ry , int * rm , int * rd );
@@ -467,7 +469,7 @@ c_find_fdoy(int y, double sg, int *rjd, int *ns)
467469 int d , rm , rd ;
468470
469471 /* Fast path: pure Gregorian calendar */
470- if (isinf (sg ) && sg < 0 ) {
472+ if (c_gregorian_only_p (sg )) {
471473 * rjd = c_gregorian_fdoy (y );
472474 * ns = 1 ;
473475 return 1 ;
@@ -486,7 +488,7 @@ c_find_ldoy(int y, double sg, int *rjd, int *ns)
486488 int i , rm , rd ;
487489
488490 /* Fast path: pure Gregorian calendar */
489- if (isinf (sg ) && sg < 0 ) {
491+ if (c_gregorian_only_p (sg )) {
490492 * rjd = c_gregorian_ldoy (y );
491493 * ns = 1 ;
492494 return 1 ;
@@ -519,7 +521,7 @@ c_find_ldom(int y, int m, double sg, int *rjd, int *ns)
519521 int i , rm , rd ;
520522
521523 /* Fast path: pure Gregorian calendar */
522- if (isinf (sg ) && sg < 0 ) {
524+ if (c_gregorian_only_p (sg )) {
523525 * rjd = c_gregorian_ldom_jd (y , m );
524526 * ns = 1 ;
525527 return 1 ;
@@ -537,8 +539,8 @@ c_civil_to_jd(int y, int m, int d, double sg, int *rjd, int *ns)
537539{
538540 int jd ;
539541
540- /* Fast path: pure Gregorian calendar (sg == -infinity) */
541- if (isinf (sg ) && sg < 0 ) {
542+ /* Fast path: pure Gregorian calendar */
543+ if (c_gregorian_only_p (sg )) {
542544 * rjd = c_gregorian_civil_to_jd (y , m , d );
543545 * ns = 1 ;
544546 return ;
@@ -570,7 +572,7 @@ static void
570572c_jd_to_civil (int jd , double sg , int * ry , int * rm , int * rdom )
571573{
572574 /* Fast path: pure Gregorian or date after switchover, within safe range */
573- if ((( isinf ( sg ) && sg < 0 ) || jd >= sg ) && ns_jd_in_range (jd )) {
575+ if ((c_gregorian_only_p ( sg ) || jd >= sg ) && ns_jd_in_range (jd )) {
574576 c_gregorian_jd_to_civil (jd , ry , rm , rdom );
575577 return ;
576578 }
@@ -789,7 +791,40 @@ c_gregorian_last_day_of_month(int y, int m)
789791 */
790792
791793/* JDN of March 1, Year 0 in proleptic Gregorian calendar */
792- #define NS_GREGORIAN_EPOCH 1721120
794+ #define NS_EPOCH 1721120
795+
796+ /* Days in a 4-year cycle (3 normal years + 1 leap year) */
797+ #define NS_DAYS_IN_4_YEARS 1461
798+
799+ /* Days in a 400-year Gregorian cycle (97 leap years in 400 years) */
800+ #define NS_DAYS_IN_400_YEARS 146097
801+
802+ /* Years per century */
803+ #define NS_YEARS_PER_CENTURY 100
804+
805+ /*
806+ * Multiplier for extracting year within century using fixed-point arithmetic.
807+ * This is ceil(2^32 / NS_DAYS_IN_4_YEARS) for the Euclidean affine function.
808+ */
809+ #define NS_YEAR_MULTIPLIER 2939745
810+
811+ /*
812+ * Coefficients for month calculation from day-of-year.
813+ * Maps day-of-year to month using: month = (NS_MONTH_COEFF * doy + NS_MONTH_OFFSET) >> 16
814+ */
815+ #define NS_MONTH_COEFF 2141
816+ #define NS_MONTH_OFFSET 197913
817+
818+ /*
819+ * Coefficients for civil date to JDN month contribution.
820+ * Maps month to accumulated days: days = (NS_CIVIL_MONTH_COEFF * m - NS_CIVIL_MONTH_OFFSET) / 32
821+ */
822+ #define NS_CIVIL_MONTH_COEFF 979
823+ #define NS_CIVIL_MONTH_OFFSET 2919
824+ #define NS_CIVIL_MONTH_DIVISOR 32
825+
826+ /* Days from March 1 to December 31 (for Jan/Feb year adjustment) */
827+ #define NS_DAYS_BEFORE_NEW_YEAR 306
793828
794829/*
795830 * Safe bounds for Neri-Schneider algorithm to avoid integer overflow.
@@ -815,48 +850,57 @@ c_gregorian_civil_to_jd(int y, int m, int d)
815850 int d0 = d - 1 ;
816851
817852 /* Calculate year contribution with leap year correction */
818- int q1 = DIV (y0 , 100 );
819- int yc = DIV (1461 * y0 , 4 ) - q1 + DIV (q1 , 4 );
853+ int q1 = DIV (y0 , NS_YEARS_PER_CENTURY );
854+ int yc = DIV (NS_DAYS_IN_4_YEARS * y0 , 4 ) - q1 + DIV (q1 , 4 );
820855
821856 /* Calculate month contribution using integer arithmetic */
822- int mc = (979 * m0 - 2919 ) / 32 ;
857+ int mc = (NS_CIVIL_MONTH_COEFF * m0 - NS_CIVIL_MONTH_OFFSET ) / NS_CIVIL_MONTH_DIVISOR ;
823858
824859 /* Combine and add epoch offset to get JDN */
825- return yc + mc + d0 + NS_GREGORIAN_EPOCH ;
860+ return yc + mc + d0 + NS_EPOCH ;
826861}
827862
828863/* Optimized: Julian Day Number -> Gregorian date */
829864static void
830865c_gregorian_jd_to_civil (int jd , int * ry , int * rm , int * rd )
831866{
832867 int r0 , n1 , q1 , r1 , n2 , q2 , r2 , n3 , q3 , r3 , y0 , j ;
833- uint64_t u2 ;
868+ #ifdef HAVE_LONG_LONG
869+ unsigned LONG_LONG u2 ;
870+ #endif
834871
835872 /* Convert JDN to rata die (March 1, Year 0 epoch) */
836- r0 = jd - NS_GREGORIAN_EPOCH ;
873+ r0 = jd - NS_EPOCH ;
837874
838875 /* Extract century and day within 400-year cycle */
839876 /* Use Euclidean (floor) division for negative values */
840877 n1 = 4 * r0 + 3 ;
841- q1 = DIV (n1 , 146097 ); /* Century */
842- r1 = MOD (n1 , 146097 ) / 4 ; /* Day within 400-year cycle */
878+ q1 = DIV (n1 , NS_DAYS_IN_400_YEARS );
879+ r1 = MOD (n1 , NS_DAYS_IN_400_YEARS ) / 4 ;
843880
844- /* Use 64-bit arithmetic for year calculation within century */
881+ /* Calculate year within century and day of year */
845882 n2 = 4 * r1 + 3 ;
846- u2 = (uint64_t )2939745 * (uint64_t )n2 ;
847- q2 = (int )(u2 >> 32 ); /* Year within century */
848- r2 = (int )((uint32_t )u2 / 2939745 / 4 ); /* Day of year */
883+ #ifdef HAVE_LONG_LONG
884+ /* Use 64-bit arithmetic to avoid overflow */
885+ u2 = (unsigned LONG_LONG )NS_YEAR_MULTIPLIER * (unsigned LONG_LONG )n2 ;
886+ q2 = (int )(u2 >> 32 );
887+ r2 = (int )((unsigned int )u2 / NS_YEAR_MULTIPLIER / 4 );
888+ #else
889+ /* Fallback for systems without 64-bit integers */
890+ q2 = n2 / NS_DAYS_IN_4_YEARS ;
891+ r2 = (n2 % NS_DAYS_IN_4_YEARS ) / 4 ;
892+ #endif
849893
850894 /* Calculate month and day using integer arithmetic */
851- n3 = 2141 * r2 + 197913 ;
852- q3 = n3 >> 16 ; /* Month (3-14) */
853- r3 = (n3 & 0xFFFF ) / 2141 ; /* Day of month (0-based) */
895+ n3 = NS_MONTH_COEFF * r2 + NS_MONTH_OFFSET ;
896+ q3 = n3 >> 16 ;
897+ r3 = (n3 & 0xFFFF ) / NS_MONTH_COEFF ;
854898
855899 /* Combine century and year */
856- y0 = 100 * q1 + q2 ;
900+ y0 = NS_YEARS_PER_CENTURY * q1 + q2 ;
857901
858902 /* Adjust for January/February (shift from fiscal year) */
859- j = (r2 >= 306 ) ? 1 : 0 ;
903+ j = (r2 >= NS_DAYS_BEFORE_NEW_YEAR ) ? 1 : 0 ;
860904
861905 * ry = y0 + j ;
862906 * rm = j ? q3 - 12 : q3 ;
0 commit comments