# Strftime's %C and %y formats versus wide-ranging tm_year values

Paul Eggert eggert at CS.UCLA.EDU
Mon Sep 27 05:16:46 UTC 2004

```Here's a patch to fix the mishandling of large tm_year values that I
noted in my previous email.  It assumes that you've already applied
the strftime.c patch (in email from ado dated 2004-09-23 19:39:37 UTC)
that increments strftime.c's elsieid from 7.67 to 7.69.

*** tzfile.h	1997/12/29 14:31:51	1997.9
--- tzfile.h	2004/09/27 04:35:02	1997.9.0.2
***************
*** 157,167 ****
#define EPOCH_WDAY	TM_THURSDAY

/*
! ** Accurate only for the past couple of centuries;
** that will probably do.
*/

#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))

#ifndef USG

--- 157,176 ----
#define EPOCH_WDAY	TM_THURSDAY

/*
! ** Accurate only for the proleptic Gregorian calendar;
** that will probably do.
+ ** isleap(y) is 1 if y is a leap year.
+ ** ismult_sum(y1, y2, d) is 1 if y1 + y2 is a multiple of d (d > 1), and
+ **	returns the correct answer even if the addition would overflow.
+ ** isleap_sum(y1, y2) equals isleap(y1 + y2) except that it also
+ **	returns the correct answer even if the addition would overflow.
*/

#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+ #define ismult_sum(y1, y2, d) ((((y1) % (d) + (y2) % (d))) % (d) == 0)
+ #define isleap_sum(y1, y2) \
+   (ismult_sum(y1, y2, 4) \
+    && (!ismult_sum(y1, y2, 100) || ismult_sum(y1, y2, 400)))

#ifndef USG

*** strftime.c	2004/09/23 19:39:37	2004.4.0.1
--- strftime.c	2004/09/27 04:35:02	2004.4.0.2
***************
*** 108,114 ****

static char *	_add P((const char *, char *, const char *));
static char *	_conv P((int, const char *, char *, const char *));
! static char *	_lconv P((long, const char *, char *, const char *));
static char *	_fmt P((const char *, const struct tm *, char *, const char *, int *));

size_t strftime P((char *, size_t, const char *, const struct tm *));
--- 108,114 ----

static char *	_add P((const char *, char *, const char *));
static char *	_conv P((int, const char *, char *, const char *));
! static char *	_yconv P((int, int, int, int, char *, const char *));
static char *	_fmt P((const char *, const struct tm *, char *, const char *, int *));

size_t strftime P((char *, size_t, const char *, const struct tm *));
***************
*** 211,230 ****
** something completely different.
*/
! 				{
! 					long	year;
! 					int	top;
!
! 					year = t->tm_year;
! 					year += TM_YEAR_BASE;
! 					top = year / 100;
! 					if (top == 0 && year < 0) {
! 						pt = _add("-0", pt, ptlim);
! 					} else {
! 						pt = _conv(top, "%02d",
! 							pt, ptlim);
! 					}
! 				}
continue;
case 'c':
{
--- 211,218 ----
** something completely different.
*/
! 				pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
! 					    pt, ptlim);
continue;
case 'c':
{
***************
*** 392,404 ****
*/
{
! 					long	year;
int	yday;
int	wday;
int	w;

year = t->tm_year;
! 					year += TM_YEAR_BASE;
yday = t->tm_yday;
wday = t->tm_wday;
for ( ; ; ) {
--- 380,393 ----
*/
{
! 					int	year;
! 					int	base;
int	yday;
int	wday;
int	w;

year = t->tm_year;
! 					base = TM_YEAR_BASE;
yday = t->tm_yday;
wday = t->tm_wday;
for ( ; ; ) {
***************
*** 406,412 ****
int	bot;
int	top;

! 						len = isleap(year) ?
DAYSPERLYEAR :
DAYSPERNYEAR;
/*
--- 395,401 ----
int	bot;
int	top;

! 						len = isleap_sum(year, base) ?
DAYSPERLYEAR :
DAYSPERNYEAR;
/*
***************
*** 425,431 ****
top += DAYSPERWEEK;
top += len;
if (yday >= top) {
! 							++year;
w = 1;
break;
}
--- 414,420 ----
top += DAYSPERWEEK;
top += len;
if (yday >= top) {
! 							++base;
w = 1;
break;
}
***************
*** 434,441 ****
DAYSPERWEEK);
break;
}
! 						--year;
! 						yday += isleap(year) ?
DAYSPERLYEAR :
DAYSPERNYEAR;
}
--- 423,430 ----
DAYSPERWEEK);
break;
}
! 						--base;
! 						yday += isleap_sum(year,base) ?
DAYSPERLYEAR :
DAYSPERNYEAR;
}
***************
*** 450,465 ****
pt = _conv(w, "%02d",
pt, ptlim);
else if (*format == 'g') {
- 						int	i;
-
*warnp = IN_ALL;
! 						i = year % 100;
! 						if (i < 0) {
! 							i = -i;
! 						}
! 						pt = _conv(i, "%02d",
pt, ptlim);
! 					} else	pt = _lconv(year, "%04ld",
pt, ptlim);
}
continue;
--- 439,448 ----
pt = _conv(w, "%02d",
pt, ptlim);
else if (*format == 'g') {
*warnp = IN_ALL;
! 						pt = _yconv(year, base, 0, 1,
pt, ptlim);
! 					} else	pt = _yconv(year, base, 1, 1,
pt, ptlim);
}
continue;
***************
*** 497,516 ****
continue;
case 'y':
*warnp = IN_ALL;
! 				{
! 					int	i;
!
! 					i = (t->tm_year +
! 						(long) TM_YEAR_BASE) % 100;
! 					if (i < 0) {
! 						i = -i;
! 					}
! 					pt = _conv(i, "%02d", pt, ptlim);
! 				}
continue;
case 'Y':
! 				pt = _lconv(t->tm_year + (long) TM_YEAR_BASE,
! 					"%04ld", pt, ptlim);
continue;
case 'Z':
#ifdef TM_ZONE
--- 480,491 ----
continue;
case 'y':
*warnp = IN_ALL;
! 				pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
! 					    pt, ptlim);
continue;
case 'Y':
! 				pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
! 					    pt, ptlim);
continue;
case 'Z':
#ifdef TM_ZONE
***************
*** 615,630 ****
}

static char *
! _lconv(n, format, pt, ptlim)
! const long		n;
! const char * const	format;
! char * const		pt;
const char * const	ptlim;
{
! 	char	buf[INT_STRLEN_MAXIMUM(long) + 1];

! 	(void) sprintf(buf, format, n);
! 	return _add(buf, pt, ptlim);
}

static char *
--- 590,638 ----
}

static char *
! _yconv(y1, y2, convert_top, convert_yy, pt, ptlim)
! const int		y1;
! const int		y2;
! const int		convert_top;
! const int		convert_yy;
! char *			pt;
const char * const	ptlim;
{
! 	/*
! 	** POSIX and the C Standard are unclear or inconsistent about
! 	** what %C and %y do if the year is negative or exceeds 9999.
! 	** Use the convention that %C concatenated with %y yields the
! 	** same output as %Y, and that %Y contains at least 4 bytes,
! 	** with more only if necessary.
! 	*/

! 	int	top, yy;
!
! 	yy = y1 % 100 + y2 % 100;
! 	top = y1 / 100 + y2 / 100 + yy / 100;
! 	yy %= 100;
!
! 	if (0 < top && yy < 0) {
! 		yy += 100;
! 		top--;
! 	} else if (top < 0 && 0 < yy) {
! 		yy -= 100;
! 		top++;
! 	}
!
! 	if (convert_top) {
! 		if (top == 0 && yy < 0) {
! 			pt = _add("-0", pt, ptlim);
! 		} else {
! 			pt = _conv(top, "%02d", pt, ptlim);
! 		}
! 	}
!
! 	if (convert_yy) {
! 		pt = _conv(yy < 0 ? -yy : yy, "%02d", pt, ptlim);
! 	}
!
! 	return pt;
}

static char *

```