v3-0003-Allow-tzcode-to-use-stdint.h-and-inttypes.h.patch

text/x-patch

Filename: v3-0003-Allow-tzcode-to-use-stdint.h-and-inttypes.h.patch
Type: text/x-patch
Part: 3
Message: Re: Cannot find a working 64-bit integer type on Illumos

Patch

Format: format-patch
Series: patch v3-0003
Subject: Allow tzcode to use <stdint.h> and <inttypes.h>.
File+
src/timezone/localtime.c 56 54
src/timezone/README 0 14
src/timezone/zic.c 40 48
From 27462adab2f9971ce34972062e51baf7728dc3ea Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Thu, 4 Jul 2024 13:00:08 +1200
Subject: [PATCH v3 3/3] Allow tzcode to use <stdint.h> and <inttypes.h>.

Previously we removed the use of types and macros from these standard
headers when synchronizing with upstream tzcode, to that we could
continue to target C89 toolchains.  Now that all supported branches of
PostgreSQL require C99, we can drop a lot of small differences.

Reviewed-by:
Discussion: https://postgr.es/m/ME3P282MB3166F9D1F71F787929C0C7E7B6312%40ME3P282MB3166.AUSP282.PROD.OUTLOOK.COM
---
 src/timezone/README      |  14 -----
 src/timezone/localtime.c | 110 ++++++++++++++++++++-------------------
 src/timezone/zic.c       |  88 ++++++++++++++-----------------
 3 files changed, 96 insertions(+), 116 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 0bc160ea7d7..e9adfc5da04 100644
--- a/src/timezone/localtime.c
+++ b/src/timezone/localtime.c
@@ -17,6 +17,7 @@
 #include "c.h"
 
 #include <fcntl.h>
+#include <stdint.h>
 
 #include "datatype/timestamp.h"
 #include "pgtz.h"
@@ -75,20 +76,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 bool increment_overflow_time(pg_time_t *tp, int_fast32_t j);
 static int64 leapcorr(struct state const *sp, pg_time_t 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 +106,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 +115,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 +135,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;
+	int_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 +161,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 +247,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;
 
 		/*
@@ -350,8 +351,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.  */
@@ -361,7 +362,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
 			{
 				/*
 				 * Leap seconds cannot occur more than once per UTC month, and
-				 * UTC months are at least 28 days long (minus 1 second for a
+				 * UTC months are at least 2 days long (minus 1 second for a
 				 * negative leap second).  Each leap second's correction must
 				 * differ from the previous one's by 1 second.
 				 */
@@ -707,9 +708,10 @@ 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;
+	int_fast32_t secsperhour = SECSPERHOUR;
 
 	/*
 	 * 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
@@ -719,7 +721,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 * secsperhour;
 	if (*strp == ':')
 	{
 		++strp;
@@ -748,7 +750,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 +837,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,
@@ -940,8 +942,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 +1035,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 +1061,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 +1075,17 @@ tzparse(const char *name, struct state *sp, bool lastditch)
 			yearlim = yearbeg + YEARSPERREPEAT + 1;
 			for (year = yearbeg; year < yearlim; year++)
 			{
-				int32
-							starttime = transtime(year, &start, stdoffset),
-							endtime = transtime(year, &end, dstoffset);
-				int32
-							yearsecs = (year_lengths[isleap(year)]
-										* SECSPERDAY);
+				int_fast32_t
+					starttime = transtime(year, &start, stdoffset),
+					endtime = transtime(year, &end, dstoffset);
+				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 +1128,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 +1292,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 +1356,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,7 +1413,7 @@ 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;
@@ -1554,7 +1556,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
diff --git a/src/timezone/zic.c b/src/timezone/zic.c
index d605c721ecf..0a98bb0ebb6 100644
--- a/src/timezone/zic.c
+++ b/src/timezone/zic.c
@@ -11,6 +11,8 @@
 #include "postgres_fe.h"
 
 #include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
 #include <sys/stat.h>
 #include <time.h>
 
@@ -22,12 +24,16 @@
 #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;
+static zic_t const
+			ZIC_MIN = INT_FAST64_MIN,
+			ZIC_MAX = INT_FAST64_MAX,
+			ZIC32_MIN = INT32_MIN,
+			ZIC32_MAX = INT32_MAX;
+#define SCNdZIC SCNdFAST64
 
 #ifndef ZIC_MAX_ABBR_LEN_WO_WARN
-#define ZIC_MAX_ABBR_LEN_WO_WARN	6
+#define ZIC_MAX_ABBR_LEN_WO_WARN 6
 #endif							/* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
 
 #ifndef WIN32
@@ -601,7 +607,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 +616,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 +624,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;
@@ -1291,18 +1297,7 @@ infile(const char *name)
 		{
 			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 +1359,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 +1381,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:
@@ -1424,7 +1418,7 @@ gethms(char const *string, char const *errstring)
 		return 0;
 	}
 	/* Some compilers warn that this test is unsatisfiable for 32-bit ints */
-#if INT_MAX > PG_INT32_MAX
+#if INT_MAX > INT32_MAX
 	if (ZIC_MAX / SECSPERHOUR < hh)
 	{
 		error(_("time overflow"));
@@ -1670,8 +1664,7 @@ getleapdatetime(char **fields, int nfields, bool expire_line)
 	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 +1674,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 +1823,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 +1880,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 +1906,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 +1975,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;
@@ -2000,7 +1986,7 @@ convert(const int32 val, char *const buf)
 }
 
 static void
-convert64(const zic_t val, char *const buf)
+convert64(uint_fast64_t val, char *const buf)
 {
 	int			i;
 	int			shift;
@@ -2011,7 +1997,7 @@ convert64(const zic_t val, char *const buf)
 }
 
 static void
-puttzcode(const int32 val, FILE *const fp)
+puttzcode(zic_t val, FILE *const fp)
 {
 	char		buf[4];
 
@@ -2201,7 +2187,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, ZIC32_MIN, ZIC32_MAX, ats, types);
 
 	/*
 	 * Remove old file, if any, to snap links.
@@ -2263,7 +2249,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 +2257,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 +2266,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
 		{
@@ -2465,7 +2451,13 @@ writezone(const char *const name, const char *const string, char version,
 			continue;
 		}
 
-		/* PG: print current timezone abbreviations if requested */
+		/*
+		 * PG: print current timezone abbreviations if requested.  Note that
+		 * we cast to int64_t, because int_fast64_t is not required to be
+		 * compatible with INT64_FORMAT.  We can't use PRIdFAST64 here because
+		 * we override fprintf with our own implementation that might not
+		 * understand it.
+		 */
 		if (print_abbrevs && pass == 2)
 		{
 			/* Print "type" data for periods ending after print_cutoff */
@@ -2478,7 +2470,7 @@ writezone(const char *const name, const char *const string, char version,
 
 					fprintf(stdout, "%s\t" INT64_FORMAT "%s\n",
 							thisabbrev,
-							utoffs[tm],
+							(int64_t) utoffs[tm],
 							isdsts[tm] ? "\tD" : "");
 				}
 			}
@@ -2490,7 +2482,7 @@ writezone(const char *const name, const char *const string, char version,
 
 				fprintf(stdout, "%s\t" INT64_FORMAT "%s\n",
 						thisabbrev,
-						utoffs[tm],
+						(int64_t) utoffs[tm],
 						isdsts[tm] ? "\tD" : "");
 			}
 		}
@@ -2499,7 +2491,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 < ZIC32_MIN ? ZIC32_MIN : lo_time;
 
 		if (locut)
 			puttzcodepass(lo, fp, pass);
-- 
2.45.2