Commits

Sebastian Freundt  committed 53fe62e Merge

Merge branch 'next'

* next: (24 commits)
hygiene, avoid shadow decl in divrem() routine, div(3) is stdlib
chore, add build status indicator to README.md
doc, mention new reference date in format.texi as well
doc, leave a note about the Gregorian Reformation assumption in dateutils
doc, explain new reference year for daisy dates
chore, lift inlining pressure from __offs() offset finder in tzraw
chore, lift inlining pressure from divrem() helper
hygiene, rewrite tuidiv()/tuimod() calls in dt_tadd() in terms of divrem() inline
chore, hygiene, eliminate __to_unix_epoch() copy in ltrcc, use public dt_to_unix_epoch() instead
minor, avoid c99 (reverse) inlines and provide a public dt_to_unix_epoch() and dt_to_gps_epoch()
minor, allow no left hand side in tzconv.m mex
major, extend date range to 1601-01-01
cosmetics, add whitespace between code and decls
fix, lilian and julian offsets for 1753-based dates are off-by-one
fix, PEBKAC, for 1753-based daisy dates compute the number of missing (leap) days properly
fix, PEBKAC, for daisy-based epoch conversions define the offset in terms of daisy's base year
hygiene, refer to dt-core's __to_unix_epoch() in dt-core-tz-glue
fix, PEBKAC, for __jan00_daisy() and 1601 base use original YEAR arg for fix-ups
fix, PEBKAC, 1601 lilian and julian dates are off by one
minor, bring back zdiff handling (and %Z)
...

  • Participants
  • Parent commits 57cd664, e3df054

Comments (0)

Files changed (18)

 Dateutils
 =========
 
+[![Build Status](https://secure.travis-ci.org/hroptatyr/dateutils.png?branch=master)](http://travis-ci.org/hroptatyr/dateutils)
+
 Dateutils are a bunch of tools that revolve around fiddling with dates
 and times in the command line with a strong focus on use cases that
 arise when dealing with large amounts of financial data.

File contrib/tzconv.c

 	zif_t fromz;
 	zif_t toz;
 
-	if (nrhs != 3) {
+	if (nrhs != 3 || nlhs > 1) {
 		mexErrMsgTxt("invalid usage, see `help tzconv'\n");
 		return;
-	} else if (nlhs == 0) {
-		return;
 	}
 
 	/* find zones */

File info/dateutils.texi

 is the 1st Jan 1, or other convenient dates like the 1st Jan 1970 or
 1st Jan 1900.
 
-The dateutils reference day (at the moment) is the 31st Dec 1916, or
-equivalently the Naught-th of Jan 1917.  This may look like an
+The (internal) dateutils reference day is the 31st Dec 1600, or
+equivalently the Naught-th of Jan 1601.  This may look like an
 arbitrary date but it has some interesting properties:
 - it is a Sunday, so weekday computation is easy as it is a mod 7
   operation only
-- the year is 1 mod 4, so the number of leap years between 1917 and
-  another year Y is simply Y div 4.
+- the year is 1 mod 4, so the number of leap years between the reference
+  year and any other year Y is simply Y div 4.
 
-Actually any date that suffices these properties is fine but 1917 is
-the first after 1900, and dates <= 1900 that have different leap year
-rules so cannot be as easily used as our reference date.
+In earlier versions we used 1917 for the reference year because it's the
+first year after 1900 that meets the conditions above.  But then we
+relaxed leap year rules to support dates beyond 2100 (2100 isn't a leap
+year) so for symmetry reasons we should support years below 1917 in a
+similar fashion.
 
 The daisy calendar has no notion of years or months.
 
 soon as Julian dates are supported, daisy will use its in/out format
 specs.
 
+Also noteworthy on a more informative level, dateutils globally assumes
+the Gregorian date switch to have taken place in October 1582.  This is
+unlike some other well-known tools (Unix cal for instance) which assume
+the reformation to have occurred in September 1752.
+
 @section bizda
 
 This calendric system is actually a whole tribe of calendars, in its

File info/format.texi

   b   Treat date as business date
 @end verbatim
 
-By design dates before 1917-01-01 are not supported.
+By design dates before 1601-01-01 are not supported.
 
 For conformity here is a list of calendar spec names and their meaning:
 @verbatim
 		by += (year - 2001U) / 400U;
 	}
 #elif DT_DAISY_BASE_YEAR == 1753
+	if (LIKELY(year > 1800U)) {
+		by -= (year - 1701U) / 100U;
+		by += (year - 1601U) / 400U;
+	}
+#elif DT_DAISY_BASE_YEAR == 1601
 	by -= (year - 1601U) / 100U;
 	by += (year - 1601U) / 400U;
-#elif DT_DAISY_BASE_YEAR == 1601
-	by -= by / 100U;
-	by += by / 400U;
 #endif
 	return by;
 #endif	/* WITH_FAST_ARITH */
 # define DT_LDN_BASE	(122068U/*lilian's 1917-01-00*/)
 # define DT_JDN_BASE	(2421228.5f/*julian's 1917-01-00*/)
 #elif DT_DAISY_BASE_YEAR == 1753
-# define DT_LDN_BASE	(62170U/*lilian's 1753-01-00*/)
-# define DT_JDN_BASE	(2361330.5f/*julian's 1753-01-00*/)
+# define DT_LDN_BASE	(62169U/*lilian's 1753-01-00*/)
+# define DT_JDN_BASE	(2361329.5f/*julian's 1753-01-00*/)
 #elif DT_DAISY_BASE_YEAR == 1601
-# define DT_LDN_BASE	(6653U/*lilian's 1601-01-00*/)
-# define DT_JDN_BASE	(2305813.5f/*julian's 1601-01-00*/)
+# define DT_LDN_BASE	(6652U/*lilian's 1601-01-00*/)
+# define DT_JDN_BASE	(2305812.5f/*julian's 1601-01-00*/)
 #else
 # error cannot convert to ldn, unknown base year
 #endif

File lib/date-core.h

 # define DT_MIN_YEAR	(1917)
 # define DT_MAX_YEAR	(2099)
 #else
-# define DT_MIN_YEAR	(1917)
+# define DT_MIN_YEAR	(1601)
 # define DT_MAX_YEAR	(4095)
 #endif	/* WITH_FAST_ARITH */
 

File lib/dt-core-tz-glue.c

 #include "nifty.h"
 #include "dt-core-tz-glue.h"
 
-#define DAISY_UNIX_BASE		(19359)
-
-static inline dt_ssexy_t
-____to_unix_epoch(struct dt_dt_s dt)
-{
-/* daisy is competing with the prevalent unix epoch, this is the offset */
-#define DAISY_UNIX_BASE		(19359)
-	if (dt.typ == DT_SEXY) {
-		/* no way to find out, is there */
-		return dt.sexy;
-	} else if (dt_sandwich_p(dt) || dt_sandwich_only_d_p(dt)) {
-		struct dt_d_s d = dt_dconv(DT_DAISY, dt.d);
-		dt_daisy_t dd = d.daisy;
-		dt_ssexy_t res = (dd - DAISY_UNIX_BASE) * SECS_PER_DAY;
-		if (dt_sandwich_p(dt)) {
-			res += (dt.t.hms.h * 60 + dt.t.hms.m) * 60 + dt.t.hms.s;
-		}
-		return res;
-	}
-	return 0;
-}
-
 
 /**
  * Return a dt object that forgot about DT's zone and uses ZONE instead. */
-DEFUN int32_t
-dtz_forgetz(struct dt_dt_s *d, zif_t zone)
+DEFUN struct dt_dt_s
+dtz_forgetz(struct dt_dt_s d, zif_t zone)
 {
 	dt_ssexy_t d_unix;
 	dt_ssexy_t d_locl;
-	struct dt_dt_s zd;
 	int32_t zdiff;
 
-	if (dt_sandwich_only_d_p(*d) || dt_sandwich_only_t_p(*d)) {
-		return 0;
-	} else if (d->znfxd) {
+	if (dt_sandwich_only_d_p(d) || dt_sandwich_only_t_p(d)) {
+		return d;
+	} else if (d.znfxd) {
 		/* already forgotten about */
-		return 0;
+		return d;
 	}
 
 	/* convert date/time part to unix stamp */
-	d_locl = ____to_unix_epoch(*d);
+	d_locl = dt_to_unix_epoch(d);
 	d_unix = zif_utc_time(zone, d_locl);
-	zdiff = d_unix - d_locl;
+	if (LIKELY((zdiff = d_unix - d_locl))) {
+		/* let dt_dtadd() do the magic */
+		struct dt_dt_s zd = dt_dt_initialiser();
 
-	/* let dt_dtadd() do the magic */
-	zd = dt_dt_initialiser();
-
-	dt_make_t_only(&zd, DT_HMS);
-	zd.t.dur = 1;
-	zd.t.sdur = zdiff;
-	*d = dt_dtadd(*d, zd);
-	d->znfxd = 1;
-	return zdiff;
+		dt_make_t_only(&zd, DT_HMS);
+		zd.t.dur = 1;
+		zd.t.sdur = zdiff;
+		d = dt_dtadd(d, zd);
+		d.znfxd = 1;
+		if (zdiff > 0) {
+			d.zdiff = (uint16_t)(zdiff / ZDIFF_RES);
+		} else if (zdiff < 0) {
+			d.neg = 1;
+			d.zdiff = (uint16_t)(-zdiff / ZDIFF_RES);
+		}
+	}
+	return d;
 }
 
 /**
  * Return a dt object from a UTC'd DT that uses ZONE. */
-DEFUN int32_t
-dtz_enrichz(struct dt_dt_s *d, zif_t zone)
+DEFUN struct dt_dt_s
+dtz_enrichz(struct dt_dt_s d, zif_t zone)
 {
 	dt_ssexy_t d_unix;
 	dt_ssexy_t d_locl;
-	struct dt_dt_s zd;
 	int32_t zdiff;
 
-	if (dt_sandwich_only_d_p(*d) || dt_sandwich_only_t_p(*d)) {
-		return 0;
+	if (dt_sandwich_only_d_p(d) || dt_sandwich_only_t_p(d)) {
+		return d;
 	}
 
 	/* convert date/time part to unix stamp */
-	d_unix = ____to_unix_epoch(*d);
+	d_unix = dt_to_unix_epoch(d);
 	d_locl = zif_local_time(zone, d_unix);
-	zdiff = d_locl - d_unix;
+	if (LIKELY((zdiff = d_locl - d_unix))) {
+		/* let dt_dtadd() do the magic */
+		struct dt_dt_s zd = dt_dt_initialiser();
 
-	/* let dt_dtadd() do the magic */
-	zd = dt_dt_initialiser();
-
-	dt_make_t_only(&zd, DT_HMS);
-	zd.t.dur = 1;
-	zd.t.sdur = zdiff;
-	*d = dt_dtadd(*d, zd);
-	return zdiff;
+		dt_make_t_only(&zd, DT_HMS);
+		zd.t.dur = 1;
+		zd.t.sdur = zdiff;
+		d = dt_dtadd(d, zd);
+		if (zdiff > 0) {
+			d.zdiff = (uint16_t)(zdiff / ZDIFF_RES);
+		} else if (zdiff < 0) {
+			d.neg = 1;
+			d.zdiff = (uint16_t)(-zdiff / ZDIFF_RES);
+		}
+	}
+	return d;
 }
 
 #endif	/* INCLUDED_dt_core_tz_glue_c_ */

File lib/dt-core-tz-glue.h

 /**
  * Return a dt object that forgot about DT's zone and uses ZONE instead.
  * In other words: convert from locally represented DT to UTC. */
-DECLF int32_t dtz_forgetz(struct dt_dt_s *dt, zif_t zone);
+DECLF struct dt_dt_s dtz_forgetz(struct dt_dt_s dt, zif_t zone);
 
 /**
  * Return a dt object from a UTC'd DT that uses ZONE.
  * In other words: convert from UTC represented DT to local ZONE time. */
-DECLF int32_t dtz_enrichz(struct dt_dt_s *dt, zif_t zone);
+DECLF struct dt_dt_s dtz_enrichz(struct dt_dt_s dt, zif_t zone);
 
 
 #if defined INCLUDE_DT_CORE_TZ_GLUE_IMPL

File lib/dt-core.c

 
 
 /* converters and stuff */
+#if !defined DT_DAISY_BASE_YEAR
+# error daisy base year cannot be obtained
+#elif DT_DAISY_BASE_YEAR == 1917
+# define DAISY_UNIX_BASE	(19359)
+# define DAISY_GPS_BASE		(23016)
+#elif DT_DAISY_BASE_YEAR == 1753
+# define DAISY_UNIX_BASE	(79258)
+# define DAISY_GPS_BASE		(82915)
+#elif DT_DAISY_BASE_YEAR == 1601
+# define DAISY_UNIX_BASE	(134775)
+# define DAISY_GPS_BASE		(138432)
+#else
+# error unknown daisy base year
+#endif	/* DT_DAISY_BASE_YEAR */
+#if DAISY_GPS_BASE - DAISY_UNIX_BASE != 3657
+# error daisy unix and gps bases diverge
+#endif	/* static assert */
+
 static inline dt_ssexy_t
 __to_unix_epoch(struct dt_dt_s dt)
 {
 /* daisy is competing with the prevalent unix epoch, this is the offset */
-#define DAISY_UNIX_BASE		(19359)
 	if (dt.typ == DT_SEXY) {
 		/* no way to find out, is there */
 		return dt.sexy;
 	return 0;
 }
 
-static inline __attribute__((unused)) dt_ssexy_t
+/* public version */
+dt_ssexy_t
+dt_to_unix_epoch(struct dt_dt_s dt)
+{
+	return __to_unix_epoch(dt);
+}
+
+static inline dt_ssexy_t
 __to_gps_epoch(struct dt_dt_s dt)
 {
-#define DAISY_GPS_BASE		(23016)
 	if (dt.typ == DT_SEXY) {
 		/* no way to find out, is there */
 		return dt.sexy;
 	return 0;
 }
 
+/* public version */
+dt_ssexy_t
+dt_to_gps_epoch(struct dt_dt_s dt)
+{
+	return __to_gps_epoch(dt);
+}
+
 static inline struct dt_dt_s
 dt_conv_to_sexy(struct dt_dt_s dt)
 {
 __epoch_to_ymdhms(dt_ssexy_t sx)
 {
 	dt_ymdhms_t res;
+
 	res.S = sx % SECS_PER_MIN;
 	sx /= SECS_PER_MIN;
 	res.M = sx % MINS_PER_HOUR;
 }
 
 DEFUN size_t
-dt_strfdt(
-	char *restrict buf, size_t bsz, const char *fmt,
-	struct dt_dt_s that, int32_t zdiff)
+dt_strfdt(char *restrict buf, size_t bsz, const char *fmt, struct dt_dt_s that)
 {
 	struct strpdt_s d = strpdt_initialiser();
 	const char *fp;
 		d.st.m = that.t.hms.m;
 		d.st.s = that.t.hms.s;
 		d.st.ns = that.t.hms.ns;
-		d.zdiff = zdiff;
+		d.zdiff = zdiff_sec(that);
 	}
 
 	/* assign and go */

File lib/dt-core.h

 			uint16_t sandwich:1;
 			/* whether we had zone info already but fixed it */
 			uint16_t znfxd:1;
+			/* whether to be aware of leap-seconds */
+			uint16_t tai:1;
 			/* unused, pad to next ui8 */
-			uint16_t:2;
+			uint16_t:1;
 			/* duration indicator */
 			uint16_t dur:1;
 			/* negation indicator */
 			uint16_t neg:1;
-			/* whether to be aware of leap-seconds */
-			uint16_t tai:1;
+			/* we've got 6 bits left here to coincide with dt_d_s
+			 * use that and the neg flag for zdiffs
+			 * zdiff itself has 15-minute resolution,
+			 * range [0, 63] aka [00:00 16:00] */
+			uint16_t zdiff:6;
+#define ZDIFF_RES	(15U * 60U)
+
 			union {
-				uint64_t u:53;
+				uint64_t u:48;
 				dt_ymdhms_t ymdhms;
-				dt_sexy_t sexy:53;
-				dt_ssexy_t sexydur:53;
-				dt_ssexy_t sxepoch:53;
+				dt_sexy_t sexy:48;
+				dt_ssexy_t sexydur:48;
+				dt_ssexy_t sxepoch:48;
 				struct {
 #if defined WORDS_BIGENDIAN
-					int32_t corr:21;
+					int32_t corr:16;
 					int32_t soft:32;
 #else  /* !WORDS_BIGENDIAN */
 					int32_t soft:32;
-					int32_t corr:21;
+					int32_t corr:16;
 #endif	/* WORDS_BIGENDIAN */
 				};
 			};
 /**
  * Like strftime() for our dates */
 DECLF size_t
-dt_strfdt(char *restrict b, size_t z, const char *fmt, struct dt_dt_s, int32_t);
+dt_strfdt(char *restrict buf, size_t bsz, const char *fmt, struct dt_dt_s);
 
 /**
  * Parse durations as in 1w5d, etc. */
  * E.g. ymd -> %FT%T */
 DECLF void __trans_dtfmt(const char **fmt);
 
+/**
+ * Convert a dt_dt_s to an epoch difference, based on the Unix epoch. */
+extern dt_ssexy_t dt_to_unix_epoch(struct dt_dt_s);
+
+/**
+ * Convert a dt_dt_s to an epoch difference, based on the GPS epoch. */
+extern dt_ssexy_t dt_to_gps_epoch(struct dt_dt_s);
+
 
 /* some useful gimmicks, sort of */
 static inline __attribute__((pure, const)) struct dt_dt_s
 	return;
 }
 
+static inline int32_t
+zdiff_sec(struct dt_dt_s d)
+{
+	int32_t zdiff = d.zdiff * ZDIFF_RES;
+
+	if (d.neg) {
+		zdiff = -zdiff;
+	}
+	return zdiff;
+}
+
 
 #if defined INCLUDE_DATETIME_CORE_IMPL
 # include "date-core.c"
 #include "dt-core.h"
 #include "nifty.h"
 
-static inline dt_ssexy_t
-__to_unix_epoch(struct dt_dt_s dt)
-{
-/* daisy is competing with the prevalent unix epoch, this is the offset */
-#define DAISY_UNIX_BASE		(19359)
-	if (dt.typ == DT_SEXY) {
-		/* no way to find out, is there */
-		return dt.sexy;
-	} else if (dt_sandwich_p(dt) || dt_sandwich_only_d_p(dt)) {
-		struct dt_d_s d = dt_dconv(DT_DAISY, dt.d);
-		dt_daisy_t dd = d.daisy;
-		dt_ssexy_t res = (dd - DAISY_UNIX_BASE) * SECS_PER_DAY;
-		if (dt_sandwich_p(dt)) {
-			res += (dt.t.hms.h * 60 + dt.t.hms.m) * 60 + dt.t.hms.s;
-		}
-		return res;
-	}
-	return 0;
-}
-
 
 #define PROLOGUE	(-1UL)
 #define EPILOGUE	(0UL)
 
 	/* fix up and convert to target type */
 	d.t.hms.s--;
-	val = __to_unix_epoch(d);
+	val = dt_to_unix_epoch(d);
 
 	if (!colp) {
 		switch (ep[1]) {

File lib/time-core.c

 }
 
 /* arithmetics helpers */
-static inline unsigned int
-__tuimod(signed int x, signed int m)
-{
-	int res = x % m;
-	return res >= 0 ? res : res + m;
-}
+struct divrem_s {
+	signed int div;
+	unsigned int rem;
+};
 
-static inline signed int
-__tuidiv(signed int x, signed int m)
+static struct divrem_s
+divrem(signed int n, unsigned int mod)
 {
-/* uidiv expects its counterpart (the mod) to be computed with __uimod */
-	int res = x / m;
-	return x >= 0 ? res : x % m ? res - 1 : res;
+	register signed int _div;
+	register signed int _rem;
+
+	_div = n / (signed int)mod;
+	if ((_rem = n % (signed int)mod) < 0) {
+		_div--;
+		_rem += mod;
+	}
+	return (struct divrem_s){_div, (unsigned int)_rem};
 }
 
 
 DEFUN struct dt_t_s
 dt_tadd(struct dt_t_s t, struct dt_t_s dur, int corr)
 {
-	unsigned int res;
-	signed int tmp;
+	signed int sec;
+	struct divrem_s tmp;
 
 	/* get both result in seconds since midnight */
-	res = (t.hms.h * MINS_PER_HOUR + t.hms.m) * SECS_PER_MIN + t.hms.s;
-	res = res + dur.sdur;
+	sec = (t.hms.h * MINS_PER_HOUR + t.hms.m) * SECS_PER_MIN + t.hms.s;
+	sec = sec + dur.sdur;
 
 	if (LIKELY(corr == 0)) {
-		tmp = __tuidiv(res, SECS_PER_DAY);
-		res = __tuimod(res, SECS_PER_DAY);
+		tmp = divrem(sec, SECS_PER_DAY);
 
 		/* fill up biggest first */
-		t.hms.h = res / SECS_PER_HOUR;
-		res = res % SECS_PER_HOUR;
-		t.hms.m = res / SECS_PER_MIN;
-		res = res % SECS_PER_MIN;
-		t.hms.s = res;
+		t.hms.h = tmp.rem / SECS_PER_HOUR;
+		tmp.rem %= SECS_PER_HOUR;
+		t.hms.m = tmp.rem / SECS_PER_MIN;
+		tmp.rem %= SECS_PER_MIN;
+		t.hms.s = tmp.rem;
 	} else {
 		/* doesn't work if we span more than 1 day */
-		tmp = __tuidiv(res, SECS_PER_DAY + corr);
-		res = __tuimod(res, SECS_PER_DAY + corr);
+		tmp = divrem(sec, SECS_PER_DAY + corr);
 
 		/* fill up biggest first */
-		if (res < SECS_PER_DAY) {
-			t.hms.h = res / SECS_PER_HOUR;
-			res = res % SECS_PER_HOUR;
-			t.hms.m = res / SECS_PER_MIN;
-			res = res % SECS_PER_MIN;
-			t.hms.s = res;
+		if (LIKELY(tmp.rem < SECS_PER_DAY)) {
+			t.hms.h = tmp.rem / SECS_PER_HOUR;
+			tmp.rem %= SECS_PER_HOUR;
+			t.hms.m = tmp.rem / SECS_PER_MIN;
+			tmp.rem %= SECS_PER_MIN;
+			t.hms.s = tmp.rem;
 		} else {
-			/* corr < 0 will always end up in the above case */
+			/* leap-second day case
+			 * corr < 0 will always end up in the above case */
 			t.hms.h = 23;
 			t.hms.m = 59;
 			t.hms.s = 59 + corr;
 	t.typ = DT_HMS;
 	t.dur = 0;
 	t.neg = 0;
-	t.carry = tmp;
+	t.carry = tmp.div;
 	return t;
 }
 
 	return __tai_offs(t) - gps_offs_epoch;
 }
 
-static inline int32_t
+static int32_t
 __offs(zif_t z, int32_t t)
 {
 /* return the offset of T in Z and cache the result. */
 
 			if (clo->hackz == NULL && clo->fromz != NULL) {
 				/* fixup zone */
-				(void)dtz_forgetz(&d, clo->fromz);
+				d = dtz_forgetz(d, clo->fromz);
 			}
 
 			if (clo->sed_mode_p) {
 
 			if (clo->hackz == NULL && clo->fromz != NULL) {
 				/* fixup zone */
-				(void)dtz_forgetz(&d, clo->fromz);
+				d = dtz_forgetz(d, clo->fromz);
 			}
 
 			/* no sed mode here */
 		if (!dt_unk_p(d = dadd_add(d, st.durs, st.ndurs))) {
 			if (hackz == NULL && fromz != NULL) {
 				/* fixup zone */
-				(void)dtz_forgetz(&d, fromz);
+				d = dtz_forgetz(d, fromz);
 			}
 			dt_io_write(d, ofmt, z, '\n');
 			res = 0;
 	switch (kv->sp.spfl) {
 	case DT_SPFL_N_STD: {
 		char buf[32];
-		dt_strfdt(buf, sizeof(buf), NULL, kv->d, 0);
+		dt_strfdt(buf, sizeof(buf), NULL, kv->d);
 		fputs(buf, stdout);
 		break;
 	}

File src/dround.c

 
 			if (ctx.hackz == NULL && ctx.fromz != NULL) {
 				/* fixup zone */
-				(void)dtz_forgetz(&d, ctx.fromz);
+				d = dtz_forgetz(d, ctx.fromz);
 			}
 
 			if (ctx.sed_mode_p) {
 		if (!dt_unk_p(d = dround(d, st.durs, st.ndurs, nextp))) {
 			if (hackz == NULL && fromz != NULL) {
 				/* fixup zone */
-				(void)dtz_forgetz(&d, fromz);
+				d = dtz_forgetz(d, fromz);
 			}
 			dt_io_write(d, ofmt, z, '\n');
 			res = 0;
 		}
 	}
 	if (LIKELY(!dt_unk_p(res)) && zone != NULL) {
-		(void)dtz_forgetz(&res, zone);
+		return dtz_forgetz(res, zone);
 	}
 	return res;
 }
 found:
 	*sp = (char*)p;
 	if (LIKELY(!dt_unk_p(d)) && zone != NULL) {
-		(void)dtz_forgetz(&d, zone);
+		return dtz_forgetz(d, zone);
 	}
 	return d;
 }
 static inline size_t
 dt_io_strfdt(
 	char *restrict buf, size_t bsz,
-	const char *fmt, struct dt_dt_s that, int32_t zdiff, int apnd_ch)
+	const char *fmt, struct dt_dt_s that, int apnd_ch)
 {
-	size_t res = dt_strfdt(buf, bsz, fmt, that, zdiff);
+	size_t res = dt_strfdt(buf, bsz, fmt, that);
 
 	if (LIKELY(res > 0) && apnd_ch && buf[res - 1] != apnd_ch) {
 		/* auto-newline */
 {
 	static char buf[256];
 	size_t n;
-	int32_t zd = 0;
 
 	if (LIKELY(!dt_unk_p(d)) && zone != NULL) {
-		zd = dtz_enrichz(&d, zone);
+		d = dtz_enrichz(d, zone);
 	}
-	n = dt_io_strfdt(buf, sizeof(buf), fmt, d, zd, apnd_ch);
+	n = dt_io_strfdt(buf, sizeof(buf), fmt, d, apnd_ch);
 	__io_write(buf, n, stdout);
 	return (n > 0) - 1;
 }

File test/struct-7.c

 		uint64_t l;
 		uint64_t h;
 	} uint128_t;
+#define __uint128_t_defined
 #endif	/* !uint128_t */
+#if !defined __uint160_t_defined
+	typedef struct {
+		uint32_t l[5];
+	} uint160_t;
+#define __uint160_t_defined
+#endif	/* !uint160_t */
 
 #define CHECK_SIZE(x, y)						\
 	if (sizeof(x) != sizeof(y)) {					\
-		fprintf(stderr, "sizeof(" #x ") -> %zu\n", sizeof(x));	\
+		fprintf(						\
+			stderr,						\
+			"sizeof(" #x ") -> %zu\t"			\
+			"sizeof(" #y ") -> %zu\n",			\
+			sizeof(x), sizeof(y));				\
 		res = 1;						\
 	}