Update timezone to C99

Started by Peter Eisentraut2 months ago7 messages
#1Peter Eisentraut
peter@eisentraut.org
1 attachment(s)

The code in src/timezone/ still contained workarounds for not wanting to
rely on C99 <stdint.h> and <inttypes.h>, even though the rest of the
code uses those now.

To fix that, I re-downloaded the upstream code (same version as before),
applied the fixes described in src/timezone/README, except those related
to the integer types, and then put back the PostgreSQL-specific code and
massaged things to remove irrelevant differences. There were a few
other minor and cosmetic changes that I left in to improve alignment
with upstream.

Patch attached. Seems to work.

Attachments:

0001-Update-timezone-to-C99.patchtext/plain; charset=UTF-8; name=0001-Update-timezone-to-C99.patchDownload
From a4b1e4c1e6e9e13561cc74d3e9625caf55d55f10 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Wed, 12 Nov 2025 17:04:02 +0100
Subject: [PATCH] Update timezone to C99

This reverts changes done in PostgreSQL over the upstream code to
avoid relying on C99 <stdint.h> and <inttypes.h>.
---
 src/timezone/README              |  14 ----
 src/timezone/localtime.c         | 117 ++++++++++++++-------------
 src/timezone/pgtz.h              |   4 +-
 src/timezone/private.h           |  28 +++----
 src/timezone/zic.c               | 135 ++++++++++++++-----------------
 src/tools/pgindent/typedefs.list |   3 +
 6 files changed, 138 insertions(+), 163 deletions(-)

diff --git a/src/timezone/README b/src/timezone/README
index dd5d5f9892a..1857f03e3dd 100644
--- a/src/timezone/README
+++ b/src/timezone/README
@@ -79,13 +79,6 @@ fixed that.)
 includes relying on configure's results rather than hand-hacked
 #defines (see private.h in particular).
 
-* Similarly, avoid relying on <stdint.h> features that may not exist on old
-systems.  In particular this means using Postgres' definitions of the int32
-and int64 typedefs, not int_fast32_t/int_fast64_t.  Likewise we use
-PG_INT32_MIN/MAX not INT32_MIN/MAX.  (Once we desupport all PG versions
-that don't require C99, it'd be practical to rely on <stdint.h> and remove
-this set of diffs; but that day is not yet.)
-
 * Since Postgres is typically built on a system that has its own copy
 of the <time.h> functions, we must avoid conflicting with those.  This
 mandates renaming typedef time_t to pg_time_t, and similarly for most
@@ -119,13 +112,6 @@ to first run the tzcode source files through a sed filter like this:
         -e 's|^\*/| */|' \
         -e 's/\bregister[ \t]//g' \
         -e 's/\bATTRIBUTE_PURE[ \t]//g' \
-        -e 's/int_fast32_t/int32/g' \
-        -e 's/int_fast64_t/int64/g' \
-        -e 's/intmax_t/int64/g' \
-        -e 's/INT32_MIN/PG_INT32_MIN/g' \
-        -e 's/INT32_MAX/PG_INT32_MAX/g' \
-        -e 's/INTMAX_MIN/PG_INT64_MIN/g' \
-        -e 's/INTMAX_MAX/PG_INT64_MAX/g' \
         -e 's/struct[ \t]+tm\b/struct pg_tm/g' \
         -e 's/\btime_t\b/pg_time_t/g' \
         -e 's/lineno/lineno_t/g' \
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c
index 8eb02ef1469..20147f1a2c2 100644
--- a/src/timezone/localtime.c
+++ b/src/timezone/localtime.c
@@ -66,7 +66,7 @@ enum r_type
 {
 	JULIAN_DAY,					/* Jn = Julian day */
 	DAY_OF_YEAR,				/* n = day of year */
-	MONTH_NTH_DAY_OF_WEEK,		/* Mm.n.d = month, week, day of week */
+	MONTH_NTH_DAY_OF_WEEK		/* Mm.n.d = month, week, day of week */
 };
 
 struct rule
@@ -75,20 +75,20 @@ struct rule
 	int			r_day;			/* day number of rule */
 	int			r_week;			/* week number of rule */
 	int			r_mon;			/* month number of rule */
-	int32		r_time;			/* transition time of rule */
+	int_fast32_t r_time;		/* transition time of rule */
 };
 
 /*
  * Prototypes for static functions.
  */
 
-static struct pg_tm *gmtsub(pg_time_t const *timep, int32 offset,
+static struct pg_tm *gmtsub(pg_time_t const *timep, int_fast32_t offset,
 							struct pg_tm *tmp);
 static bool increment_overflow(int *ip, int j);
-static bool increment_overflow_time(pg_time_t *tp, int32 j);
-static int64 leapcorr(struct state const *sp, pg_time_t t);
+static bool increment_overflow_time(pg_time_t *tp, int_fast32_t j);
+static int_fast64_t leapcorr(struct state const *sp, pg_time_t);
 static struct pg_tm *timesub(pg_time_t const *timep,
-							 int32 offset, struct state const *sp,
+							 int_fast32_t offset, struct state const *sp,
 							 struct pg_tm *tmp);
 static bool typesequiv(struct state const *sp, int a, int b);
 
@@ -105,7 +105,7 @@ static struct pg_tm tm;
 
 /* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX.  */
 static void
-init_ttinfo(struct ttinfo *s, int32 utoff, bool isdst, int desigidx)
+init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, int desigidx)
 {
 	s->tt_utoff = utoff;
 	s->tt_isdst = isdst;
@@ -114,15 +114,15 @@ init_ttinfo(struct ttinfo *s, int32 utoff, bool isdst, int desigidx)
 	s->tt_ttisut = false;
 }
 
-static int32
+static int_fast32_t
 detzcode(const char *const codep)
 {
-	int32		result;
+	int_fast32_t result;
 	int			i;
-	int32		one = 1;
-	int32		halfmaxval = one << (32 - 2);
-	int32		maxval = halfmaxval - 1 + halfmaxval;
-	int32		minval = -1 - maxval;
+	int_fast32_t one = 1;
+	int_fast32_t halfmaxval = one << (32 - 2);
+	int_fast32_t maxval = halfmaxval - 1 + halfmaxval;
+	int_fast32_t minval = -1 - maxval;
 
 	result = codep[0] & 0x7f;
 	for (i = 1; i < 4; ++i)
@@ -134,21 +134,21 @@ detzcode(const char *const codep)
 		 * Do two's-complement negation even on non-two's-complement machines.
 		 * If the result would be minval - 1, return minval.
 		 */
-		result -= !TWOS_COMPLEMENT(int32) && result != 0;
+		result -= !TWOS_COMPLEMENT(int_fast32_t) && result != 0;
 		result += minval;
 	}
 	return result;
 }
 
-static int64
+static int_fast64_t
 detzcode64(const char *const codep)
 {
-	uint64		result;
+	uint_fast64_t result;
 	int			i;
-	int64		one = 1;
-	int64		halfmaxval = one << (64 - 2);
-	int64		maxval = halfmaxval - 1 + halfmaxval;
-	int64		minval = -TWOS_COMPLEMENT(int64) - maxval;
+	int_fast64_t one = 1;
+	int_fast64_t halfmaxval = one << (64 - 2);
+	int_fast64_t maxval = halfmaxval - 1 + halfmaxval;
+	int_fast64_t minval = -TWOS_COMPLEMENT(int_fast64_t) - maxval;
 
 	result = codep[0] & 0x7f;
 	for (i = 1; i < 8; ++i)
@@ -160,7 +160,7 @@ detzcode64(const char *const codep)
 		 * Do two's-complement negation even on non-two's-complement machines.
 		 * If the result would be minval - 1, return minval.
 		 */
-		result -= !TWOS_COMPLEMENT(int64) && result != 0;
+		result -= !TWOS_COMPLEMENT(int_fast64_t) && result != 0;
 		result += minval;
 	}
 	return result;
@@ -246,14 +246,14 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
 		return errno;
 	for (stored = 4; stored <= 8; stored *= 2)
 	{
-		int32		ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
-		int32		ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
-		int64		prevtr = 0;
-		int32		prevcorr = 0;
-		int32		leapcnt = detzcode(up->tzhead.tzh_leapcnt);
-		int32		timecnt = detzcode(up->tzhead.tzh_timecnt);
-		int32		typecnt = detzcode(up->tzhead.tzh_typecnt);
-		int32		charcnt = detzcode(up->tzhead.tzh_charcnt);
+		int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
+		int_fast32_t ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
+		int_fast64_t prevtr = 0;
+		int_fast32_t prevcorr = 0;
+		int_fast32_t leapcnt = detzcode(up->tzhead.tzh_leapcnt);
+		int_fast32_t timecnt = detzcode(up->tzhead.tzh_timecnt);
+		int_fast32_t typecnt = detzcode(up->tzhead.tzh_typecnt);
+		int_fast32_t charcnt = detzcode(up->tzhead.tzh_charcnt);
 		char const *p = up->buf + tzheadsize;
 
 		/*
@@ -291,7 +291,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
 		timecnt = 0;
 		for (i = 0; i < sp->timecnt; ++i)
 		{
-			int64		at
+			int_fast64_t at
 			= stored == 4 ? detzcode(p) : detzcode64(p);
 
 			sp->types[i] = at <= TIME_T_MAX;
@@ -350,8 +350,8 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
 		leapcnt = 0;
 		for (i = 0; i < sp->leapcnt; ++i)
 		{
-			int64		tr = stored == 4 ? detzcode(p) : detzcode64(p);
-			int32		corr = detzcode(p + stored);
+			int_fast64_t tr = stored == 4 ? detzcode(p) : detzcode64(p);
+			int_fast32_t corr = detzcode(p + stored);
 
 			p += stored + 4;
 			/* Leap seconds cannot occur before the Epoch.  */
@@ -421,6 +421,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
 		up->buf[nread - 1] = '\0';
 		if (tzparse(&up->buf[1], ts, false))
 		{
+
 			/*
 			 * Attempt to reuse existing abbreviations. Without this,
 			 * America/Anchorage would be right on the edge after 2037 when
@@ -583,7 +584,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
  * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
  */
 int
-tzload(const char *name, char *canonname, struct state *sp, bool doextend)
+tzload(char const *name, char *canonname, struct state *sp, bool doextend)
 {
 	union local_storage *lsp = malloc(sizeof *lsp);
 
@@ -707,7 +708,7 @@ getnum(const char *strp, int *const nump, const int min, const int max)
  */
 
 static const char *
-getsecs(const char *strp, int32 *const secsp)
+getsecs(const char *strp, int_fast32_t *const secsp)
 {
 	int			num;
 
@@ -719,7 +720,7 @@ getsecs(const char *strp, int32 *const secsp)
 	strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
 	if (strp == NULL)
 		return NULL;
-	*secsp = num * (int32) SECSPERHOUR;
+	*secsp = num * (int_fast32_t) SECSPERHOUR;
 	if (*strp == ':')
 	{
 		++strp;
@@ -748,7 +749,7 @@ getsecs(const char *strp, int32 *const secsp)
  */
 
 static const char *
-getoffset(const char *strp, int32 *const offsetp)
+getoffset(const char *strp, int_fast32_t *const offsetp)
 {
 	bool		neg = false;
 
@@ -835,12 +836,12 @@ getrule(const char *strp, struct rule *const rulep)
  * effect, calculate the year-relative time that rule takes effect.
  */
 
-static int32
+static int_fast32_t
 transtime(const int year, const struct rule *const rulep,
-		  const int32 offset)
+		  const int_fast32_t offset)
 {
 	bool		leapyear;
-	int32		value;
+	int_fast32_t value;
 	int			i;
 	int			d,
 				m1,
@@ -905,7 +906,7 @@ transtime(const int year, const struct rule *const rulep,
 			for (i = 1; i < rulep->r_week; ++i)
 			{
 				if (d + DAYSPERWEEK >=
-					mon_lengths[(int) leapyear][rulep->r_mon - 1])
+					mon_lengths[leapyear][rulep->r_mon - 1])
 					break;
 				d += DAYSPERWEEK;
 			}
@@ -915,7 +916,7 @@ transtime(const int year, const struct rule *const rulep,
 			 */
 			value = d * SECSPERDAY;
 			for (i = 0; i < rulep->r_mon - 1; ++i)
-				value += mon_lengths[(int) leapyear][i] * SECSPERDAY;
+				value += mon_lengths[leapyear][i] * SECSPERDAY;
 			break;
 	}
 
@@ -940,8 +941,8 @@ tzparse(const char *name, struct state *sp, bool lastditch)
 	size_t		stdlen;
 	size_t		dstlen;
 	size_t		charcnt;
-	int32		stdoffset;
-	int32		dstoffset;
+	int_fast32_t stdoffset;
+	int_fast32_t dstoffset;
 	char	   *cp;
 	bool		load_ok;
 
@@ -1033,7 +1034,7 @@ tzparse(const char *name, struct state *sp, bool lastditch)
 			int			yearlim;
 			int			timecnt;
 			pg_time_t	janfirst;
-			int32		janoffset = 0;
+			int_fast32_t janoffset = 0;
 			int			yearbeg;
 
 			++name;
@@ -1059,7 +1060,7 @@ tzparse(const char *name, struct state *sp, bool lastditch)
 
 			do
 			{
-				int32		yearsecs
+				int_fast32_t yearsecs
 				= year_lengths[isleap(yearbeg - 1)] * SECSPERDAY;
 
 				yearbeg--;
@@ -1073,17 +1074,17 @@ tzparse(const char *name, struct state *sp, bool lastditch)
 			yearlim = yearbeg + YEARSPERREPEAT + 1;
 			for (year = yearbeg; year < yearlim; year++)
 			{
-				int32
+				int_fast32_t
 							starttime = transtime(year, &start, stdoffset),
 							endtime = transtime(year, &end, dstoffset);
-				int32
+				int_fast32_t
 							yearsecs = (year_lengths[isleap(year)]
 										* SECSPERDAY);
 				bool		reversed = endtime < starttime;
 
 				if (reversed)
 				{
-					int32		swap = starttime;
+					int_fast32_t swap = starttime;
 
 					starttime = endtime;
 					endtime = swap;
@@ -1126,9 +1127,9 @@ tzparse(const char *name, struct state *sp, bool lastditch)
 		}
 		else
 		{
-			int32		theirstdoffset;
-			int32		theirdstoffset;
-			int32		theiroffset;
+			int_fast32_t theirstdoffset;
+			int_fast32_t theirdstoffset;
+			int_fast32_t theiroffset;
 			bool		isdst;
 			int			i;
 			int			j;
@@ -1290,7 +1291,7 @@ localsub(struct state const *sp, pg_time_t const *timep,
 		result = localsub(sp, &newt, tmp);
 		if (result)
 		{
-			int64		newy;
+			int_fast64_t newy;
 
 			newy = result->tm_year;
 			if (t < sp->ats[0])
@@ -1354,7 +1355,7 @@ pg_localtime(const pg_time_t *timep, const pg_tz *tz)
  */
 
 static struct pg_tm *
-gmtsub(pg_time_t const *timep, int32 offset,
+gmtsub(pg_time_t const *timep, int_fast32_t offset,
 	   struct pg_tm *tmp)
 {
 	struct pg_tm *result;
@@ -1411,16 +1412,16 @@ leaps_thru_end_of(const int y)
 }
 
 static struct pg_tm *
-timesub(const pg_time_t *timep, int32 offset,
+timesub(const pg_time_t *timep, int_fast32_t offset,
 		const struct state *sp, struct pg_tm *tmp)
 {
 	const struct lsinfo *lp;
 	pg_time_t	tdays;
 	int			idays;			/* unsigned would be so 2003 */
-	int64		rem;
+	int_fast64_t rem;
 	int			y;
 	const int  *ip;
-	int64		corr;
+	int_fast64_t corr;
 	bool		hit;
 	int			i;
 
@@ -1554,7 +1555,7 @@ increment_overflow(int *ip, int j)
 }
 
 static bool
-increment_overflow_time(pg_time_t *tp, int32 j)
+increment_overflow_time(pg_time_t *tp, int_fast32_t j)
 {
 	/*----------
 	 * This is like
@@ -1570,7 +1571,7 @@ increment_overflow_time(pg_time_t *tp, int32 j)
 	return false;
 }
 
-static int64
+static int_fast64_t
 leapcorr(struct state const *sp, pg_time_t t)
 {
 	struct lsinfo const *lp;
diff --git a/src/timezone/pgtz.h b/src/timezone/pgtz.h
index 8caac631cab..ff595ce974a 100644
--- a/src/timezone/pgtz.h
+++ b/src/timezone/pgtz.h
@@ -25,7 +25,7 @@
 
 struct ttinfo
 {								/* time type information */
-	int32		tt_utoff;		/* UT offset in seconds */
+	int_fast32_t tt_utoff;		/* UT offset in seconds */
 	bool		tt_isdst;		/* used to set tm_isdst */
 	int			tt_desigidx;	/* abbreviation list index */
 	bool		tt_ttisstd;		/* transition is std time */
@@ -35,7 +35,7 @@ struct ttinfo
 struct lsinfo
 {								/* leap second information */
 	pg_time_t	ls_trans;		/* transition time */
-	int64		ls_corr;		/* correction to apply */
+	int_fast64_t ls_corr;		/* correction to apply */
 };
 
 struct state
diff --git a/src/timezone/private.h b/src/timezone/private.h
index 39d40e43a9f..ab89028f3e1 100644
--- a/src/timezone/private.h
+++ b/src/timezone/private.h
@@ -44,10 +44,6 @@
 /* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
 #define is_digit(c) ((unsigned)(c) - '0' <= 9)
 
-/* PG doesn't currently rely on <inttypes.h>, so work around strtoimax() */
-#undef strtoimax
-#define strtoimax strtoll
-
 
 /*
  * Finally, some convenience items.
@@ -95,25 +91,25 @@
 #define YEARSPERREPEAT		400 /* years before a Gregorian repeat */
 
 #define SECSPERMIN	60
-#define MINSPERHOUR 60
-#define HOURSPERDAY 24
-#define DAYSPERWEEK 7
+#define MINSPERHOUR	60
+#define HOURSPERDAY	24
+#define DAYSPERWEEK	7
 #define DAYSPERNYEAR	365
 #define DAYSPERLYEAR	366
-#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
-#define SECSPERDAY	((int32) SECSPERHOUR * HOURSPERDAY)
-#define MONSPERYEAR 12
+#define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY	((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR	12
 
 #define TM_SUNDAY	0
 #define TM_MONDAY	1
 #define TM_TUESDAY	2
 #define TM_WEDNESDAY	3
-#define TM_THURSDAY 4
+#define TM_THURSDAY	4
 #define TM_FRIDAY	5
-#define TM_SATURDAY 6
+#define TM_SATURDAY	6
 
 #define TM_JANUARY	0
-#define TM_FEBRUARY 1
+#define TM_FEBRUARY	1
 #define TM_MARCH	2
 #define TM_APRIL	3
 #define TM_MAY		4
@@ -122,8 +118,8 @@
 #define TM_AUGUST	7
 #define TM_SEPTEMBER	8
 #define TM_OCTOBER	9
-#define TM_NOVEMBER 10
-#define TM_DECEMBER 11
+#define TM_NOVEMBER	10
+#define TM_DECEMBER	11
 
 #define TM_YEAR_BASE	1900
 
@@ -153,7 +149,7 @@
 
 #define AVGSECSPERYEAR		31556952L
 #define SECSPERREPEAT \
-  ((int64) YEARSPERREPEAT * (int64) AVGSECSPERYEAR)
+  ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
 #define SECSPERREPEAT_BITS	34	/* ceil(log2(SECSPERREPEAT)) */
 
 #endif							/* !defined PRIVATE_H */
diff --git a/src/timezone/zic.c b/src/timezone/zic.c
index 3b70b888180..a8c1de9910d 100644
--- a/src/timezone/zic.c
+++ b/src/timezone/zic.c
@@ -22,9 +22,11 @@
 #define	ZIC_VERSION_PRE_2013 '2'
 #define	ZIC_VERSION	'3'
 
-typedef int64 zic_t;
-#define ZIC_MIN PG_INT64_MIN
-#define ZIC_MAX PG_INT64_MAX
+typedef int_fast64_t zic_t;
+#define ZIC_MIN INT_FAST64_MIN
+#define ZIC_MAX INT_FAST64_MAX
+#define PRIdZIC PRIdFAST64
+#define SCNdZIC SCNdFAST64
 
 #ifndef ZIC_MAX_ABBR_LEN_WO_WARN
 #define ZIC_MAX_ABBR_LEN_WO_WARN	6
@@ -47,12 +49,15 @@ typedef int64 zic_t;
 static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t));
 #endif
 
-/*
- * The type for line numbers.  In Postgres, use %d to format them; upstream
- * uses PRIdMAX but we prefer not to rely on that, not least because it
- * results in platform-dependent strings to be translated.
- */
-typedef int lineno_t;
+/* The minimum alignment of a type, for pre-C11 platforms.  */
+#if __STDC_VERSION__ < 201112
+#define _Alignof(type) offsetof(struct { char a; type b; }, b)
+#endif
+
+/* The type for line numbers.  Use PRIdMAX to format them; formerly
+   there was also "#define PRIdLINENO PRIdMAX" and formats used
+   PRIdLINENO, but xgettext cannot grok that.  */
+typedef intmax_t lineno_t;
 
 struct rule
 {
@@ -117,18 +122,16 @@ extern int	link(const char *target, const char *linkname);
 	(itssymlink(target) ? (errno = ENOTSUP, -1) : link(target, linkname))
 #endif
 
-pg_noreturn static void memory_exhausted(const char *msg);
-static void verror(const char *string, va_list args) pg_attribute_printf(1, 0);
-static void error(const char *string,...) pg_attribute_printf(1, 2);
-static void warning(const char *string,...) pg_attribute_printf(1, 2);
-pg_noreturn static void usage(FILE *stream, int status);
+static void verror(const char *const string, va_list args) pg_attribute_printf(1, 0);
+static void error(const char *const string,...) pg_attribute_printf(1, 2);
+static void warning(const char *const string,...) pg_attribute_printf(1, 2);
 static void addtt(zic_t starttime, int type);
 static int	addtype(zic_t utoff, char const *abbr,
 					bool isdst, bool ttisstd, bool ttisut);
 static void leapadd(zic_t t, int correction, int rolling);
 static void adjleap(void);
 static void associate(void);
-static void dolink(char const *target, char const *linkname,
+static void dolink(const char *target, const char *linkname,
 				   bool staysymlink);
 static char **getfields(char *cp);
 static zic_t gethms(const char *string, const char *errstring);
@@ -407,7 +410,7 @@ static char roll[TZ_MAX_LEAPS];
  * Memory allocation.
  */
 
-static void
+static _Noreturn void
 memory_exhausted(const char *msg)
 {
 	fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg);
@@ -422,6 +425,17 @@ size_product(size_t nitems, size_t itemsize)
 	return nitems * itemsize;
 }
 
+static size_t
+align_to(size_t size, size_t alignment)
+{
+	size_t		aligned_size = size + alignment - 1;
+
+	aligned_size -= aligned_size % alignment;
+	if (aligned_size < size)
+		memory_exhausted(_("alignment overflow"));
+	return aligned_size;
+}
+
 static void *
 memcheck(void *ptr)
 {
@@ -485,23 +499,23 @@ eat(char const *name, lineno_t num)
 }
 
 static void
-verror(const char *string, va_list args)
+verror(const char *const string, va_list args)
 {
 	/*
 	 * Match the format of "cc" to allow sh users to zic ... 2>&1 | error -t
 	 * "*" -v on BSD systems.
 	 */
 	if (filename)
-		fprintf(stderr, _("\"%s\", line %d: "), filename, linenum);
+		fprintf(stderr, _("\"%s\", line %" PRIdMAX ": "), filename, linenum);
 	vfprintf(stderr, string, args);
 	if (rfilename != NULL)
-		fprintf(stderr, _(" (rule from \"%s\", line %d)"),
+		fprintf(stderr, _(" (rule from \"%s\", line %" PRIdMAX ")"),
 				rfilename, rlinenum);
 	fprintf(stderr, "\n");
 }
 
 static void
-error(const char *string,...)
+error(const char *const string,...)
 {
 	va_list		args;
 
@@ -512,7 +526,7 @@ error(const char *string,...)
 }
 
 static void
-warning(const char *string,...)
+warning(const char *const string,...)
 {
 	va_list		args;
 
@@ -539,7 +553,7 @@ close_file(FILE *stream, char const *dir, char const *name)
 	}
 }
 
-static void
+static _Noreturn void
 usage(FILE *stream, int status)
 {
 	fprintf(stream,
@@ -601,7 +615,7 @@ static zic_t comment_leapexpires = -1;
 static bool
 timerange_option(char *timerange)
 {
-	int64		lo = min_time,
+	intmax_t	lo = min_time,
 				hi = max_time;
 	char	   *lo_end = timerange,
 			   *hi_end;
@@ -610,7 +624,7 @@ timerange_option(char *timerange)
 	{
 		errno = 0;
 		lo = strtoimax(timerange + 1, &lo_end, 10);
-		if (lo_end == timerange + 1 || (lo == PG_INT64_MAX && errno == ERANGE))
+		if (lo_end == timerange + 1 || (lo == INTMAX_MAX && errno == ERANGE))
 			return false;
 	}
 	hi_end = lo_end;
@@ -618,9 +632,9 @@ timerange_option(char *timerange)
 	{
 		errno = 0;
 		hi = strtoimax(lo_end + 2, &hi_end, 10);
-		if (hi_end == lo_end + 2 || hi == PG_INT64_MIN)
+		if (hi_end == lo_end + 2 || hi == INTMAX_MIN)
 			return false;
-		hi -= !(hi == PG_INT64_MAX && errno == ERANGE);
+		hi -= !(hi == INTMAX_MAX && errno == ERANGE);
 	}
 	if (*hi_end || hi < lo || max_time < lo || hi < min_time)
 		return false;
@@ -1290,20 +1304,7 @@ infile(const char *name)
 		if (nfields == 0)
 		{
 			if (name == leapsec && *buf == '#')
-			{
-				/*
-				 * PG: INT64_FORMAT isn't portable for sscanf, so be content
-				 * with scanning a "long".  Once we are requiring C99 in all
-				 * live branches, it'd be sensible to adopt upstream's
-				 * practice of using the <inttypes.h> macros.  But for now, we
-				 * don't actually use this code, and it won't overflow before
-				 * 2038 anyway.
-				 */
-				long		cl_tmp;
-
-				sscanf(buf, "#expires %ld", &cl_tmp);
-				comment_leapexpires = cl_tmp;
-			}
+				sscanf(buf, "#expires %" SCNdZIC, &comment_leapexpires);
 		}
 		else if (wantcont)
 		{
@@ -1364,8 +1365,7 @@ infile(const char *name)
 static zic_t
 gethms(char const *string, char const *errstring)
 {
-	/* PG: make hh be int not zic_t to avoid sscanf portability issues */
-	int			hh;
+	zic_t		hh;
 	int			sign,
 				mm = 0,
 				ss = 0;
@@ -1387,7 +1387,7 @@ gethms(char const *string, char const *errstring)
 	else
 		sign = 1;
 	switch (sscanf(string,
-				   "%d%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
+				   "%" SCNdZIC "%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
 				   &hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs))
 	{
 		default:
@@ -1423,19 +1423,16 @@ gethms(char const *string, char const *errstring)
 		error("%s", errstring);
 		return 0;
 	}
-	/* Some compilers warn that this test is unsatisfiable for 32-bit ints */
-#if INT_MAX > PG_INT32_MAX
 	if (ZIC_MAX / SECSPERHOUR < hh)
 	{
 		error(_("time overflow"));
 		return 0;
 	}
-#endif
 	ss += 5 + ((ss ^ 1) & (xr == '0')) <= tenths;	/* Round to even.  */
 	if (noise && (hh > HOURSPERDAY ||
 				  (hh == HOURSPERDAY && (mm != 0 || ss != 0))))
 		warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
-	return oadd(sign * (zic_t) hh * SECSPERHOUR,
+	return oadd(sign * hh * SECSPERHOUR,
 				sign * (mm * SECSPERMIN + ss));
 }
 
@@ -1543,7 +1540,7 @@ inzone(char **fields, int nfields)
 			strcmp(zones[i].z_name, fields[ZF_NAME]) == 0)
 		{
 			error(_("duplicate zone name %s"
-					" (file \"%s\", line %d)"),
+					" (file \"%s\", line %" PRIdMAX ")"),
 				  fields[ZF_NAME],
 				  zones[i].z_filename,
 				  zones[i].z_linenum);
@@ -1669,9 +1666,7 @@ getleapdatetime(char **fields, int nfields, bool expire_line)
 	const struct lookup *lp;
 	zic_t		i,
 				j;
-
-	/* PG: make year be int not zic_t to avoid sscanf portability issues */
-	int			year;
+	zic_t		year;
 	int			month,
 				day;
 	zic_t		dayoff,
@@ -1681,7 +1676,7 @@ getleapdatetime(char **fields, int nfields, bool expire_line)
 
 	dayoff = 0;
 	cp = fields[LP_YEAR];
-	if (sscanf(cp, "%d%c", &year, &xs) != 1)
+	if (sscanf(cp, "%" SCNdZIC "%c", &year, &xs) != 1)
 	{
 		/*
 		 * Leapin' Lizards!
@@ -1830,9 +1825,6 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
 	char	   *ep;
 	char		xs;
 
-	/* PG: year_tmp is to avoid sscanf portability issues */
-	int			year_tmp;
-
 	if ((lp = byword(monthp, mon_names)) == NULL)
 	{
 		error(_("invalid month name"));
@@ -1890,9 +1882,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
 						progname, lp->l_value);
 				exit(EXIT_FAILURE);
 		}
-	else if (sscanf(cp, "%d%c", &year_tmp, &xs) == 1)
-		rp->r_loyear = year_tmp;
-	else
+	else if (sscanf(cp, "%" SCNdZIC "%c", &rp->r_loyear, &xs) != 1)
 	{
 		error(_("invalid starting year"));
 		return;
@@ -1918,9 +1908,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
 						progname, lp->l_value);
 				exit(EXIT_FAILURE);
 		}
-	else if (sscanf(cp, "%d%c", &year_tmp, &xs) == 1)
-		rp->r_hiyear = year_tmp;
-	else
+	else if (sscanf(cp, "%" SCNdZIC "%c", &rp->r_hiyear, &xs) != 1)
 	{
 		error(_("invalid ending year"));
 		return;
@@ -1989,7 +1977,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
 }
 
 static void
-convert(const int32 val, char *const buf)
+convert(const int_fast32_t val, char *const buf)
 {
 	int			i;
 	int			shift;
@@ -2011,7 +1999,7 @@ convert64(const zic_t val, char *const buf)
 }
 
 static void
-puttzcode(const int32 val, FILE *const fp)
+puttzcode(const int_fast32_t val, FILE *const fp)
 {
 	char		buf[4];
 
@@ -2097,7 +2085,8 @@ writezone(const char *const name, const char *const string, char version,
 	 * Allocate the ATS and TYPES arrays via a single malloc, as this is a bit
 	 * faster.
 	 */
-	zic_t	   *ats = emalloc(MAXALIGN(size_product(nats, sizeof *ats + 1)));
+	zic_t	   *ats = emalloc(align_to(size_product(nats, sizeof *ats + 1),
+									   _Alignof(zic_t)));
 	void	   *typesptr = ats + nats;
 	unsigned char *types = typesptr;
 	struct timerange rangeall,
@@ -2201,7 +2190,7 @@ writezone(const char *const name, const char *const string, char version,
 	rangeall.count = timecnt;
 	rangeall.leapcount = leapcnt;
 	range64 = limitrange(rangeall, lo_time, hi_time, ats, types);
-	range32 = limitrange(range64, PG_INT32_MIN, PG_INT32_MAX, ats, types);
+	range32 = limitrange(range64, INT32_MIN, INT32_MAX, ats, types);
 
 	/*
 	 * Remove old file, if any, to snap links.
@@ -2263,7 +2252,7 @@ writezone(const char *const name, const char *const string, char version,
 			/*
 			 * Arguably the default time type in the 32-bit data should be
 			 * range32.defaulttype, which is suited for timestamps just before
-			 * PG_INT32_MIN.  However, zic traditionally used the time type of
+			 * INT32_MIN.  However, zic traditionally used the time type of
 			 * the indefinite past instead.  Internet RFC 8532 says readers
 			 * should ignore 32-bit data, so this discrepancy matters only to
 			 * obsolete readers where the traditional type might be more
@@ -2271,7 +2260,7 @@ writezone(const char *const name, const char *const string, char version,
 			 * value, unless -r specifies a low cutoff that excludes some
 			 * 32-bit timestamps.
 			 */
-			thisdefaulttype = (lo_time <= PG_INT32_MIN
+			thisdefaulttype = (lo_time <= INT32_MIN
 							   ? range64.defaulttype
 							   : range32.defaulttype);
 
@@ -2280,8 +2269,8 @@ writezone(const char *const name, const char *const string, char version,
 			toomanytimes = thistimecnt >> 31 >> 1 != 0;
 			thisleapi = range32.leapbase;
 			thisleapcnt = range32.leapcount;
-			locut = PG_INT32_MIN < lo_time;
-			hicut = hi_time < PG_INT32_MAX;
+			locut = INT32_MIN < lo_time;
+			hicut = hi_time < INT32_MAX;
 		}
 		else
 		{
@@ -2476,7 +2465,7 @@ writezone(const char *const name, const char *const string, char version,
 					unsigned char tm = types[i];
 					char	   *thisabbrev = &thischars[indmap[desigidx[tm]]];
 
-					fprintf(stdout, "%s\t" INT64_FORMAT "%s\n",
+					fprintf(stdout, "%s\t%" PRIdFAST64 "%s\n",
 							thisabbrev,
 							utoffs[tm],
 							isdsts[tm] ? "\tD" : "");
@@ -2488,7 +2477,7 @@ writezone(const char *const name, const char *const string, char version,
 				unsigned char tm = defaulttype;
 				char	   *thisabbrev = &thischars[indmap[desigidx[tm]]];
 
-				fprintf(stdout, "%s\t" INT64_FORMAT "%s\n",
+				fprintf(stdout, "%s\t%" PRIdFAST64 "%s\n",
 						thisabbrev,
 						utoffs[tm],
 						isdsts[tm] ? "\tD" : "");
@@ -2499,7 +2488,7 @@ writezone(const char *const name, const char *const string, char version,
 		 * Output a LO_TIME transition if needed; see limitrange. But do not
 		 * go below the minimum representable value for this pass.
 		 */
-		lo = pass == 1 && lo_time < PG_INT32_MIN ? PG_INT32_MIN : lo_time;
+		lo = pass == 1 && lo_time < INT32_MIN ? INT32_MIN : lo_time;
 
 		if (locut)
 			puttzcodepass(lo, fp, pass);
@@ -3753,7 +3742,7 @@ getfields(char *cp)
 	return array;
 }
 
-static void
+static _Noreturn void
 time_overflow(void)
 {
 	error(_("time overflow"));
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 432509277c9..a9e89ac92de 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3739,6 +3739,8 @@ int64_t
 int8
 int8_t
 int8x16_t
+int_fast32_t
+int_fast64_t
 internalPQconninfoOption
 intptr_t
 intset_internal_node
@@ -4192,6 +4194,7 @@ uint64x2_t
 uint8
 uint8_t
 uint8x16_t
+uint_fast64_t
 uintptr_t
 unicodeStyleBorderFormat
 unicodeStyleColumnFormat
-- 
2.51.0

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#1)
Re: Update timezone to C99

Peter Eisentraut <peter@eisentraut.org> writes:

To fix that, I re-downloaded the upstream code (same version as before),
applied the fixes described in src/timezone/README, except those related
to the integer types, and then put back the PostgreSQL-specific code and
massaged things to remove irrelevant differences. There were a few
other minor and cosmetic changes that I left in to improve alignment
with upstream.

Hm, I've had "re-sync TZ code with upstream" on my TODO list for
several years now. I believe there's been quite a bit of churn
upstream since tzcode2020d, some of it oriented towards this same
issue of code modernization. Maybe we should try to sync with
a newer release while we're at it.

regards, tom lane

#3Peter Eisentraut
peter@eisentraut.org
In reply to: Tom Lane (#2)
Re: Update timezone to C99

On 12.11.25 19:02, Tom Lane wrote:

Peter Eisentraut <peter@eisentraut.org> writes:

To fix that, I re-downloaded the upstream code (same version as before),
applied the fixes described in src/timezone/README, except those related
to the integer types, and then put back the PostgreSQL-specific code and
massaged things to remove irrelevant differences. There were a few
other minor and cosmetic changes that I left in to improve alignment
with upstream.

Hm, I've had "re-sync TZ code with upstream" on my TODO list for
several years now. I believe there's been quite a bit of churn
upstream since tzcode2020d, some of it oriented towards this same
issue of code modernization. Maybe we should try to sync with
a newer release while we're at it.

My idea was to do this C99 adjustment first so that the differences to
upstream are reduced, which would hopefully simplify updating to that
newer code.

#4Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#3)
Re: Update timezone to C99

Peter Eisentraut <peter@eisentraut.org> writes:

On 12.11.25 19:02, Tom Lane wrote:

Hm, I've had "re-sync TZ code with upstream" on my TODO list for
several years now. I believe there's been quite a bit of churn
upstream since tzcode2020d, some of it oriented towards this same
issue of code modernization. Maybe we should try to sync with
a newer release while we're at it.

My idea was to do this C99 adjustment first so that the differences to
upstream are reduced, which would hopefully simplify updating to that
newer code.

Fair enough. I looked through the patch briefly and had a couple of
minor quibbles:

@@ -905,7 +906,7 @@ transtime(const int year, const struct rule *const rulep,
 			for (i = 1; i < rulep->r_week; ++i)
 			{
 				if (d + DAYSPERWEEK >=
-					mon_lengths[(int) leapyear][rulep->r_mon - 1])
+					mon_lengths[leapyear][rulep->r_mon - 1])
 					break;
 				d += DAYSPERWEEK;
 			}
@@ -915,7 +916,7 @@ transtime(const int year, const struct rule *const rulep,
 			 */
 			value = d * SECSPERDAY;
 			for (i = 0; i < rulep->r_mon - 1; ++i)
-				value += mon_lengths[(int) leapyear][i] * SECSPERDAY;
+				value += mon_lengths[leapyear][i] * SECSPERDAY;
 			break;
 	}

"leapyear" is bool, and I believe these casts-to-int were put in to
suppress compiler bleats about up-casting that to int. This probably
dates from when we equated bool to char, and maybe it's moot now,
but I'm not sure.

@@ -47,12 +49,15 @@ typedef int64 zic_t;
static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t));
#endif

-/*
- * The type for line numbers.  In Postgres, use %d to format them; upstream
- * uses PRIdMAX but we prefer not to rely on that, not least because it
- * results in platform-dependent strings to be translated.
- */
-typedef int lineno_t;
+/* The minimum alignment of a type, for pre-C11 platforms.  */
+#if __STDC_VERSION__ < 201112
+#define _Alignof(type) offsetof(struct { char a; type b; }, b)
+#endif

Since we've dropped pre-C11 support, I wonder why we'd include this
upstream workaround for that.

regards, tom lane

#5Peter Eisentraut
peter@eisentraut.org
In reply to: Tom Lane (#4)
Re: Update timezone to C99

On 14.11.25 22:11, Tom Lane wrote:

Peter Eisentraut <peter@eisentraut.org> writes:

On 12.11.25 19:02, Tom Lane wrote:

Hm, I've had "re-sync TZ code with upstream" on my TODO list for
several years now. I believe there's been quite a bit of churn
upstream since tzcode2020d, some of it oriented towards this same
issue of code modernization. Maybe we should try to sync with
a newer release while we're at it.

My idea was to do this C99 adjustment first so that the differences to
upstream are reduced, which would hopefully simplify updating to that
newer code.

Fair enough. I looked through the patch briefly and had a couple of
minor quibbles:

@@ -905,7 +906,7 @@ transtime(const int year, const struct rule *const rulep,
for (i = 1; i < rulep->r_week; ++i)
{
if (d + DAYSPERWEEK >=
-					mon_lengths[(int) leapyear][rulep->r_mon - 1])
+					mon_lengths[leapyear][rulep->r_mon - 1])
break;
d += DAYSPERWEEK;
}
@@ -915,7 +916,7 @@ transtime(const int year, const struct rule *const rulep,
*/
value = d * SECSPERDAY;
for (i = 0; i < rulep->r_mon - 1; ++i)
-				value += mon_lengths[(int) leapyear][i] * SECSPERDAY;
+				value += mon_lengths[leapyear][i] * SECSPERDAY;
break;
}

"leapyear" is bool, and I believe these casts-to-int were put in to
suppress compiler bleats about up-casting that to int. This probably
dates from when we equated bool to char, and maybe it's moot now,
but I'm not sure.

Correct, with bool as char this would have fallen afoul of
-Wchar-subscripts, which is included in -Wall.

I could remove these casts as a separate patch so that the reason can be
documented more clearly.

@@ -47,12 +49,15 @@ typedef int64 zic_t;
static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t));
#endif

-/*
- * The type for line numbers.  In Postgres, use %d to format them; upstream
- * uses PRIdMAX but we prefer not to rely on that, not least because it
- * results in platform-dependent strings to be translated.
- */
-typedef int lineno_t;
+/* The minimum alignment of a type, for pre-C11 platforms.  */
+#if __STDC_VERSION__ < 201112
+#define _Alignof(type) offsetof(struct { char a; type b; }, b)
+#endif

Since we've dropped pre-C11 support, I wonder why we'd include this
upstream workaround for that.

I figured it would be better to reduce divergences from upstream, even
if we don't need all the code.

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#5)
Re: Update timezone to C99

Peter Eisentraut <peter@eisentraut.org> writes:

On 14.11.25 22:11, Tom Lane wrote:

"leapyear" is bool, and I believe these casts-to-int were put in to
suppress compiler bleats about up-casting that to int. This probably
dates from when we equated bool to char, and maybe it's moot now,
but I'm not sure.

Correct, with bool as char this would have fallen afoul of
-Wchar-subscripts, which is included in -Wall.
I could remove these casts as a separate patch so that the reason can be
documented more clearly.

Either that or just explain it in a para of the commit message.

Since we've dropped pre-C11 support, I wonder why we'd include this
upstream workaround for that.

I figured it would be better to reduce divergences from upstream, even
if we don't need all the code.

[ shrug... ] Okay.

No further comments.

regards, tom lane

#7Peter Eisentraut
peter@eisentraut.org
In reply to: Tom Lane (#6)
Re: Update timezone to C99

On 19.11.25 16:48, Tom Lane wrote:

No further comments.

This has been committed.

FWIW, I don't have any plans currently to work on upgrading the code to
newer upstream versions.