Proposed localtime.c changes for long years

Paul Eggert eggert at CS.UCLA.EDU
Tue Sep 7 18:43:21 UTC 2004


Thanks for looking into that.  I'm responding to the proposed
localtime.c changes; a separate email will address the strftime.c
changes.

"Olson, Arthur David (NIH/NCI)" <olsona at dc37a.nci.nih.gov> writes:

> extern char *   asctime_r();

This could be a problem if asctime_r is a macro in time.h.  Also, it
should probably be moved to private.h, since it's a header fixup (like
errno) that is useful in other modules.  (Also, being an old-timer I
have some qualms about declaring externs inside blocks, due to bugs in
older compilers.)

> ! #define tm_year	USE_Y_NOT_YOURTM_TM_YEAR
>...
> + #undef tm_year

I'd remove this trick.  Strictly speaking, the C Standard doesn't
allow it: it says that identifiers in standard headers are reserved
for use as macro names.  More practically, I worry that some <time.h>
somewhere might #define tm_year to something else, and the
#define/#undef trick won't work there.  The #define/#undef isn't
needed for the code to run, so I'd leave it out.

! 	** Turn y into an actual year number for now.
  	** It is converted back to an offset from TM_YEAR_BASE later.

This hack has bothered me for a while, due to the possibility of
overflow when adding TM_YEAR_BASE.  It's a minor point, but how about
if we add a macro like this and use it instead?

#define isleap_tm(y) \
  ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 100 || (y) % 400 == -300))

isleap_tm(tm_year) equals isleap(tm_year + TM_YEAR_BASE),
except that it returns the correct answer even if the addition would overflow.

Doing this will require changing one "y + TM_YEAR_BASE < EPOCH_YEAR"
to "y < EPOCH_YEAR - TM_YEAR_BASE" (to avoid a possible overflow on
addition) in localtime.c.

Here's a patch embodying these proposed changes.

===================================================================
RCS file: RCS/tzfile.h,v
retrieving revision 1997.9
retrieving revision 1997.9.0.1
diff -pu -r1997.9 -r1997.9.0.1
--- tzfile.h	1997/12/29 14:31:51	1997.9
+++ tzfile.h	2004/09/07 18:32:29	1997.9.0.1
@@ -159,9 +159,13 @@ struct tzhead {
 /*
 ** Accurate only for the past couple of centuries;
 ** that will probably do.
+** isleap_tm(tm_year) equals isleap(tm_year + TM_YEAR_BASE),
+** except that it returns the right answer even if the addition would overflow.
 */
 
 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+#define isleap_tm(y) \
+  ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 100 || (y) % 400 == -300))
 
 #ifndef USG
 
===================================================================
RCS file: RCS/private.h,v
retrieving revision 2003.5
retrieving revision 2003.5.1.1
diff -pu -r2003.5 -r2003.5.1.1
--- private.h	2003/12/15 14:36:35	2003.5
+++ private.h	2004/09/07 18:32:29	2003.5.1.1
@@ -195,6 +195,15 @@ extern int errno;
 #endif /* !defined errno */
 
 /*
+** Some time.h implementations don't declare asctime_r.
+** Others might define it as a macro.
+** Fix the former without affecting the latter.
+*/
+#ifndef asctime_r
+extern char *	asctime_r();
+#endif
+
+/*
 ** Private function declarations.
 */
 char *	icalloc P((int nelem, int elsize));
===================================================================
RCS file: RCS/localtime.c,v
retrieving revision 2003.5.1.1
retrieving revision 2003.5.0.3
diff -pu -r2003.5.1.1 -r2003.5.0.3
--- localtime.c	2004/09/07 12:21:29	2003.5.1.1
+++ localtime.c	2004/09/07 18:32:29	2003.5.0.3
@@ -1262,7 +1262,6 @@ const time_t * const	timep;
 char *			buf;
 {
 	struct tm	tm;
-	extern char *	asctime_r();
 
 	return asctime_r(localtime_r(timep, &tm), buf);
 }
@@ -1386,32 +1385,22 @@ const int		do_norm_secs;
 	if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
 		return WRONG;
 	y = yourtm.tm_year;
-/*
-** Hands off tm_year for a while.
-*/
-#define tm_year	USE_Y_NOT_YOURTM_TM_YEAR
 	if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR))
 		return WRONG;
-	/*
-	** Turn y into an actual year number for now.
-	** It is converted back to an offset from TM_YEAR_BASE later.
-	*/
-	if (long_increment_overflow(&y, TM_YEAR_BASE))
-		return WRONG;
 	while (yourtm.tm_mday <= 0) {
 		if (long_increment_overflow(&y, -1))
 			return WRONG;
 		li = y + (1 < yourtm.tm_mon);
-		yourtm.tm_mday += year_lengths[isleap(li)];
+		yourtm.tm_mday += year_lengths[isleap_tm(li)];
 	}
 	while (yourtm.tm_mday > DAYSPERLYEAR) {
 		li = y + (1 < yourtm.tm_mon);
-		yourtm.tm_mday -= year_lengths[isleap(li)];
+		yourtm.tm_mday -= year_lengths[isleap_tm(li)];
 		if (long_increment_overflow(&y, 1))
 			return WRONG;
 	}
 	for ( ; ; ) {
-		i = mon_lengths[isleap(y)][yourtm.tm_mon];
+		i = mon_lengths[isleap_tm(y)][yourtm.tm_mon];
 		if (yourtm.tm_mday <= i)
 			break;
 		yourtm.tm_mday -= i;
@@ -1421,18 +1410,12 @@ const int		do_norm_secs;
 				return WRONG;
 		}
 	}
-	if (long_increment_overflow(&y, -TM_YEAR_BASE))
-		return WRONG;
-/*
-** Hands back on tm_year.
-*/
-#undef tm_year
 	yourtm.tm_year = y;
 	if (yourtm.tm_year != y)
  		return WRONG;
 	if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
 		saved_seconds = 0;
-	else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
+	else if (y < EPOCH_YEAR - TM_YEAR_BASE) {
 		/*
 		** We can't set tm_sec to 0, because that might push the
 		** time below the minimum representable time.



More information about the tz mailing list