Strftime's %C and %y formats versus wide-ranging tm_year valu es

Paul Eggert eggert at CS.UCLA.EDU
Fri Oct 15 19:02:52 UTC 2004


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

> Below might be a simpler asctime.c fix.

It's simpler, but I'd like to handle overflow nicely even if
STRICTLY_STANDARD_ASCTIME is defined.  Also, the "%-4s" in ASCTIME_FMT
bothers me, as the "-4" shouldn't be needed.

How about this further patch?  It shortens asctime.c further (assuming
the two asctime.c patches you've already sent).  Aside from fixing the
STRICTLY_STANDARD_ASCTIME overflow glitch, this patch avoids some
byte-scanning and some duplicated format-string contents, and changes
"digits" to "bytes" in a few comments referring to contents that might
include "-".

===================================================================
RCS file: RCS/asctime.c,v
retrieving revision 2004.3.1.3
retrieving revision 2004.3.0.5
diff -c -r2004.3.1.3 -r2004.3.0.5
*** asctime.c	2004/10/14 21:34:12	2004.3.1.3
--- asctime.c	2004/10/15 18:53:52	2004.3.0.5
***************
*** 15,44 ****
  #include "tzfile.h"
  
  #if STRICTLY_STANDARD_ASCTIME
! #define ASCTIME_FMT	"%.3s %.3s%3d %.2d:%.2d:%.2d %s\n"
! #define ASCTIME_FMT_B	ASCTIME_FMT
  #else /* !STRICTLY_STANDARD_ASCTIME */
  /*
  ** Some systems only handle "%.2d"; others only handle "%02d";
  ** "%02.2d" makes (most) everybody happy.
  ** At least some versions of gcc warn about the %02.2d; ignore the warning.
  */
  /*
  ** All years associated with 32-bit time_t values are exactly four digits long;
  ** some years associated with 64-bit time_t values are not.
! ** Vintage programs are coded for years that are always four digits long
  ** and may assume that the newline always lands in the same place.
! ** For years that are less than four digits, we pad the output with
  ** leading zeroes to get the newline in the traditional place.
! */
! #define ASCTIME_FMT	"%.3s %.3s%3d %02.2d:%02.2d:%02.2d %-4s\n"
! /*
! ** For years that are more than four digits we put extra spaces before the year
  ** so that code trying to overwrite the newline won't end up overwriting
  ** a digit within a year and truncating the year (operating on the assumption
  ** that no output is better than wrong output).
  */
! #define ASCTIME_FMT_B	"%.3s %.3s%3d %02.2d:%02.2d:%02.2d     %s\n"
  #endif /* !STRICTLY_STANDARD_ASCTIME */
  
  #define STD_ASCTIME_BUF_SIZE	26
--- 15,44 ----
  #include "tzfile.h"
  
  #if STRICTLY_STANDARD_ASCTIME
! #define ASCTIME_FMT	"%.3s %.3s%3d %.2d:%.2d:%.2d "
! #define SMALL_YEAR_FMT	"%d\n"
! #define LARGE_YEAR_TFMT	"%Y\n"
  #else /* !STRICTLY_STANDARD_ASCTIME */
  /*
  ** Some systems only handle "%.2d"; others only handle "%02d";
  ** "%02.2d" makes (most) everybody happy.
  ** At least some versions of gcc warn about the %02.2d; ignore the warning.
  */
+ #define ASCTIME_FMT	"%.3s %.3s%3d %02.2d:%02.2d:%02.2d "
  /*
  ** All years associated with 32-bit time_t values are exactly four digits long;
  ** some years associated with 64-bit time_t values are not.
! ** Vintage programs are coded for years that are always four bytes long
  ** and may assume that the newline always lands in the same place.
! ** For years that are less than four bytes, we pad the output with
  ** leading zeroes to get the newline in the traditional place.
! ** For years that are more than four bytes we put extra spaces before the year
  ** so that code trying to overwrite the newline won't end up overwriting
  ** a digit within a year and truncating the year (operating on the assumption
  ** that no output is better than wrong output).
  */
! #define SMALL_YEAR_FMT	"%-4d\n"
! #define LARGE_YEAR_TFMT	"    %Y\n"
  #endif /* !STRICTLY_STANDARD_ASCTIME */
  
  #define STD_ASCTIME_BUF_SIZE	26
***************
*** 72,80 ****
  		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  	};
  	register const char *	wn;
  	register const char *	mn;
- 	char			year[INT_STRLEN_MAXIMUM(int) + 2];
  	char			result[MAX_ASCTIME_BUF_SIZE];
  
  	if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
--- 72,80 ----
  		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  	};
+ 	register char *		pt;
  	register const char *	wn;
  	register const char *	mn;
  	char			result[MAX_ASCTIME_BUF_SIZE];
  
  	if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
***************
*** 83,113 ****
  	if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
  		mn = "???";
  	else	mn = mon_name[timeptr->tm_mon];
- #if STRICTLY_STANDARD_ASCTIME
- 	/*
- 	** Be strict, potential overflow problems included.
- 	** In an ideal world, this code is never going to be used.
- 	*/
- 	(void) sprintf(year, "%d", timeptr->tm_year + TM_YEAR_BASE);
- #else /* !STRICTLY_STANDARD_ASCTIME */
- 	/*
- 	** Use strftime's %Y to generate the year, to avoid overflow problems
- 	** when computing timeptr->tm_year + TM_YEAR_BASE.
- 	** Assume that strftime is unaffected by other out-of-range members
- 	** (e.g., timeptr->tm_mday) when processing "%Y".
- 	*/
- 	(void) strftime(year, sizeof year, "%Y", timeptr);
- #endif /* !STRICTLY_STANDARD_ASCTIME */
  	/*
  	** We avoid using snprintf since it's not available on all systems.
  	*/
! 	(void) sprintf(result, 
! 		((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
  		wn, mn,
  		timeptr->tm_mday, timeptr->tm_hour,
! 		timeptr->tm_min, timeptr->tm_sec,
! 		year);
! 	if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime) {
  		(void) strcpy(buf, result);
  		return buf;
  	} else {
--- 83,111 ----
  	if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
  		mn = "???";
  	else	mn = mon_name[timeptr->tm_mon];
  	/*
  	** We avoid using snprintf since it's not available on all systems.
  	*/
! 	pt = result + sprintf(result,
! 		ASCTIME_FMT,
  		wn, mn,
  		timeptr->tm_mday, timeptr->tm_hour,
! 		timeptr->tm_min, timeptr->tm_sec);
! 	if (-999 - TM_YEAR_BASE <= timeptr->tm_year
! 	    && timeptr->tm_year <= 9999 - TM_YEAR_BASE)
! 		pt += sprintf(pt,
! 			SMALL_YEAR_FMT, timeptr->tm_year + TM_YEAR_BASE);
! 	else
! 		/*
! 		** Use strftime's %Y to generate the year, to avoid overflow
! 		** problems when computing timeptr->tm_year + TM_YEAR_BASE.
! 		** Assume that strftime is unaffected by other out-of-range
! 		** members (e.g., timeptr->tm_mday) when processing "%Y".
! 		*/
! 		pt += strftime(pt,
! 			sizeof LARGE_YEAR_TFMT + INT_STRLEN_MAXIMUM(int) - 1,
! 			LARGE_YEAR_TFMT, timeptr);
! 	if (pt < result + STD_ASCTIME_BUF_SIZE || buf == buf_asctime) {
  		(void) strcpy(buf, result);
  		return buf;
  	} else {



More information about the tz mailing list