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.
** (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 *
More information about the tz
mailing list