[PATCH 1/1] offset: optimized calculation of year from unix timestamp

gary@garygende.com gary@garygende.com
Thu Aug 7 09:13:50 GMT 2025


---
  time/offtime.c | 40 +++++++++++++++++++++++++++-------------
  1 file changed, 27 insertions(+), 13 deletions(-)

diff --git a/time/offtime.c b/time/offtime.c
index c94573e931..aae98ccf62 100644
--- a/time/offtime.c
+++ b/time/offtime.c
@@ -20,6 +20,11 @@

  #define	SECS_PER_HOUR	(60 * 60)
  #define	SECS_PER_DAY	(SECS_PER_HOUR * 24)
+#define DAYS_OFFSET   (280 * 365 + 89 * 366)
+#define DIVISOR_400   (303 * 365 + 97 * 366)
+#define DIVISOR_100   (76 * 365 + 24 * 366)
+#define DIVISOR_4     (3 * 365 + 366)
+#define DIVISOR_1     365

  /* Compute the `struct tm' representation of T,
     offset OFFSET seconds east of UTC,
@@ -52,22 +57,31 @@ __offtime (__time64_t t, long int offset, struct tm 
*tp)
    tp->tm_wday = (4 + days) % 7;
    if (tp->tm_wday < 0)
      tp->tm_wday += 7;
-  y = 1970;

-#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
-#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
+  #define MIN_YEAR_GROUP_NUM(a,b) (((a)<(b))?(a):(b))
+  /* Start at the begining of a 400 year leapyear cycle */
+  y = 1601;
+  days += DAYS_OFFSET;

-  while (days < 0 || days >= (__isleap (y) ? 366 : 365))
-    {
-      /* Guess a corrected year, assuming 365 days per year.  */
-      __time64_t yg = y + days / 365 - (days % 365 < 0);
+  y += (days / DIVISOR_400) * 400;
+  days -= (days / DIVISOR_400) * DIVISOR_400;
+
+  /* account for 3x 100 yr cycles being 1 day shorter than a 400 yr 
cycle */
+  y += MIN_YEAR_GROUP_NUM(days / DIVISOR_100, 3) * 100;
+  days -= MIN_YEAR_GROUP_NUM(days / DIVISOR_100, 3) * DIVISOR_100;
+
+  y += (days / DIVISOR_4) * 4;
+  days -= (days / DIVISOR_4) * DIVISOR_4;
+
+  y += MIN_YEAR_GROUP_NUM(days / DIVISOR_1, 3);
+  days -= MIN_YEAR_GROUP_NUM(days / DIVISOR_1, 3) * DIVISOR_1;
+
+  if (days < 0) {
+    --y;
+    days += 365;
+    if (__isleap(y)) ++days;
+  }

-      /* Adjust DAYS and Y to match the guessed year.  */
-      days -= ((yg - y) * 365
-	       + LEAPS_THRU_END_OF (yg - 1)
-	       - LEAPS_THRU_END_OF (y - 1));
-      y = yg;
-    }
    tp->tm_year = y - 1900;
    if (tp->tm_year != y - 1900)
      {
-- 
2.43.0


More information about the Libc-alpha mailing list