Proposed strftime.c changes for long years

Paul Eggert eggert at cs.ucla.edu
Tue Sep 7 20:03:07 UTC 2004


The proposed strftime.c code for the %C conversion uses _lconv, but
the converted value can't possibly fall outside the range
INT_MIN..INT_MAX so it might be a bit more efficient to use _conv.

The %C conversion expression "(t->tm_year + (long) TM_YEAR_BASE) /
100" returns the wrong result in some cases due to overflow, e.g., if
t->tm_year == INT_MAX and INT_MAX==LONG_MAX.

It looks like you've leaning towards having %y generate year%100 even
when the year is negative, but I'd like to present a new arguemnt for
generating year mod 100 instead, on the grounds of preventing buffer
overruns in older code.  Most programmers expect strftime %y to
generate exactly two digits, in the range 00-99 as the the C Standard
explicitly requires.  Generating strings like "-1" or (especially)
"-99" might cause errors (e.g., buffer overruns) in older code.

The situation for %g is similar.

%C is less straightforward, since here the C Standard requires the
impossible (it says the output range must be 00-99).  However, I'd
argue that it's least surprising if %C is consistent with %y, i.e., if
%C is (year - (year mod 100)) / 100.

The following proposed patch addresses the above issues.

===================================================================
RCS file: RCS/strftime.c,v
retrieving revision 2001.4.1.1
retrieving revision 2001.4.0.2
diff -pu -r2001.4.1.1 -r2001.4.0.2
--- strftime.c	2004/09/07 12:21:29	2001.4.1.1
+++ strftime.c	2004/09/07 19:11:35	2001.4.0.2
@@ -211,9 +211,12 @@ label:
 				** something completely different.
 				** (ado, 1993-05-24)
 				*/
-				pt = _lconv((t->tm_year +
-					(long) TM_YEAR_BASE) / 100,
-					"%02ld", pt, ptlim);
+				{
+				int c = (TM_YEAR_BASE / 100
+					 + t->tm_year / 100
+					 - (t->tm_year % 100 < 0));
+				pt = _conv(c, "%02d", pt, ptlim);
+				}
 				continue;
 			case 'c':
 				{
@@ -439,9 +442,12 @@ label:
 						pt = _conv(w, "%02d",
 							pt, ptlim);
 					else if (*format == 'g') {
+						int g = year % 100;
+						if (g < 0)
+							g += 100;
 						*warnp = IN_ALL;
-						pt = _conv(int(year % 100),
-							"%02d", pt, ptlim);
+						pt = _conv(g, "%02d",
+							pt, ptlim);
 					} else	pt = _lconv(year, "%04ld",
 							pt, ptlim);
 				}
@@ -479,11 +485,13 @@ label:
 				}
 				continue;
 			case 'y':
+				{
+				int y = t->tm_year % 100;
+				if (y < 0)
+					  y += 100;
 				*warnp = IN_ALL;
-				pt = _conv(
-					(int) ((t->tm_year +
-					(long) TM_YEAR_BASE) % 100),
-					"%02d", pt, ptlim);
+				pt = _conv(y, "%02d", pt, ptlim);
+				}
 				continue;
 			case 'Y':
 				pt = _lconv(t->tm_year + (long) TM_YEAR_BASE,



More information about the tz mailing list