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. ** (ado, 1993-05-24) */ ! { ! 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. ** (ado, 1993-05-24) */ ! pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, ! pt, ptlim); continue; case 'c': { *************** *** 392,404 **** ** (ado, 1996-01-02) */ { ! 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 ---- ** (ado, 1996-01-02) */ { ! 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 *