[tz] [PATCH 4/8] Simplify zic integer arithmetic

Paul Eggert eggert at cs.ucla.edu
Wed Mar 24 03:58:49 UTC 2021


* private.h (DAYSPERREPEAT): New macro.
(SECSPERREPEAT, AVGSECSPERYEAR): Use it.
Rearrange REPEATish macros in a more-logical order.
* zic.c (rpytime): Avoid some conditional branches and
don’t treat years in the range 0..1969 as a special case.
The code for negative years was dubious anyway.
(LDAYSPERWEEK): Remove.  All uses removed.
---
 private.h | 18 ++++++------------
 zic.c     | 46 +++++++++++++++++-----------------------------
 2 files changed, 23 insertions(+), 41 deletions(-)

diff --git a/private.h b/private.h
index a919e4f..e38a4f3 100644
--- a/private.h
+++ b/private.h
@@ -710,8 +710,6 @@ char *ctime_r(time_t const *, char *);
 
 /* Handy macros that are independent of tzfile implementation.  */
 
-#define YEARSPERREPEAT		400	/* years before a Gregorian repeat */
-
 #define SECSPERMIN	60
 #define MINSPERHOUR	60
 #define HOURSPERDAY	24
@@ -722,6 +720,12 @@ char *ctime_r(time_t const *, char *);
 #define SECSPERDAY	((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
 #define MONSPERYEAR	12
 
+#define YEARSPERREPEAT		400	/* years before a Gregorian repeat */
+#define DAYSPERREPEAT		((int_fast32_t) 400 * 365 + 100 - 4 + 1)
+#define SECSPERREPEAT		((int_fast64_t) DAYSPERREPEAT * SECSPERDAY)
+#define SECSPERREPEAT_BITS	34	/* ceil(log2(SECSPERREPEAT)) */
+#define AVGSECSPERYEAR		(SECSPERREPEAT / YEARSPERREPEAT)
+
 #define TM_SUNDAY	0
 #define TM_MONDAY	1
 #define TM_TUESDAY	2
@@ -764,14 +768,4 @@ char *ctime_r(time_t const *, char *);
 
 #define isleap_sum(a, b)	isleap((a) % 400 + (b) % 400)
 
-
-/*
-** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
-*/
-
-#define AVGSECSPERYEAR		31556952L
-#define SECSPERREPEAT \
-  ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
-#define SECSPERREPEAT_BITS	34	/* ceil(log2(SECSPERREPEAT)) */
-
 #endif /* !defined PRIVATE_H */
diff --git a/zic.c b/zic.c
index f5d813b..6c38bd7 100644
--- a/zic.c
+++ b/zic.c
@@ -3420,6 +3420,7 @@ rpytime(const struct rule *rp, zic_t wantedy)
 	register int	m, i;
 	register zic_t	dayoff;			/* with a nod to Margaret O. */
 	register zic_t	t, y;
+	int yrem;
 
 	if (wantedy == ZIC_MIN)
 		return min_time;
@@ -3428,24 +3429,20 @@ rpytime(const struct rule *rp, zic_t wantedy)
 	dayoff = 0;
 	m = TM_JANUARY;
 	y = EPOCH_YEAR;
-	if (y < wantedy) {
-	  wantedy -= y;
-	  dayoff = (wantedy / YEARSPERREPEAT) * (SECSPERREPEAT / SECSPERDAY);
-	  wantedy %= YEARSPERREPEAT;
-	  wantedy += y;
-	} else if (wantedy < 0) {
-	  dayoff = (wantedy / YEARSPERREPEAT) * (SECSPERREPEAT / SECSPERDAY);
-	  wantedy %= YEARSPERREPEAT;
-	}
+
+	/* dayoff = floor((wantedy - y) / YEARSPERREPEAT) * DAYSPERREPEAT,
+	   sans overflow.  */
+	yrem = wantedy % YEARSPERREPEAT - y % YEARSPERREPEAT;
+	dayoff = ((wantedy / YEARSPERREPEAT - y / YEARSPERREPEAT
+		   + yrem / YEARSPERREPEAT - (yrem % YEARSPERREPEAT < 0))
+		  * DAYSPERREPEAT);
+	/* wantedy = y + ((wantedy - y) mod YEARSPERREPEAT), sans overflow.  */
+	wantedy = y + (yrem + 2 * YEARSPERREPEAT) % YEARSPERREPEAT;
+
 	while (wantedy != y) {
-		if (wantedy > y) {
-			i = len_years[isleap(y)];
-			++y;
-		} else {
-			--y;
-			i = -len_years[isleap(y)];
-		}
+		i = len_years[isleap(y)];
 		dayoff = oadd(dayoff, i);
+		y++;
 	}
 	while (m != rp->r_month) {
 		i = len_months[isleap(y)][m];
@@ -3464,30 +3461,21 @@ rpytime(const struct rule *rp, zic_t wantedy)
 	--i;
 	dayoff = oadd(dayoff, i);
 	if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
-		register zic_t	wday;
-
-#define LDAYSPERWEEK	((zic_t) DAYSPERWEEK)
-		wday = EPOCH_WDAY;
 		/*
 		** Don't trust mod of negative numbers.
 		*/
-		if (dayoff >= 0)
-			wday = (wday + dayoff) % LDAYSPERWEEK;
-		else {
-			wday -= ((-dayoff) % LDAYSPERWEEK);
-			if (wday < 0)
-				wday += LDAYSPERWEEK;
-		}
+		zic_t wday = ((EPOCH_WDAY + dayoff % DAYSPERWEEK + DAYSPERWEEK)
+			      % DAYSPERWEEK);
 		while (wday != rp->r_wday)
 			if (rp->r_dycode == DC_DOWGEQ) {
 				dayoff = oadd(dayoff, 1);
-				if (++wday >= LDAYSPERWEEK)
+				if (++wday >= DAYSPERWEEK)
 					wday = 0;
 				++i;
 			} else {
 				dayoff = oadd(dayoff, -1);
 				if (--wday < 0)
-					wday = LDAYSPERWEEK - 1;
+					wday = DAYSPERWEEK - 1;
 				--i;
 			}
 		if (i < 0 || i >= len_months[isleap(y)][m]) {
-- 
2.27.0



More information about the tz mailing list