RE: Strftime's %C and %y formats versus wide-ranging tm_year valu es
Below find the latest iteration on handling wide-ranging tm_year values. 1. The asctime function punts to strftime to generate output representing the year; the asctime documentation has been adjusted accordingly; a comment based on Paul Eggert's suggestion has been added. 2. zdump.c and strftime.c have been changed along the lines proposed by Paul Eggert; I've tried to make the code as similar as possible in the two places. I've stayed with the "% 400" version of isleap_sum since it's correct and relatively simple (if not efficient). I'd suggest not worrying about this too much. I don't know of any real-world applications where code relying on isleap is a bottleneck. I also foresee that with the extension of the range of time_t to the distant past, the days of being able to determine leap-year status with a macro are numbered. This round of changes does not split tzfile.h into caldefs.h and tzfile.h. --ado diff -c -r old/asctime.c new/asctime.c *** old/asctime.c Mon Oct 11 14:47:03 2004 --- new/asctime.c Thu Oct 14 13:36:37 2004 *************** *** 5,11 **** #ifndef lint #ifndef NOID ! static char elsieid[] = "@(#)asctime.c 7.22"; #endif /* !defined NOID */ #endif /* !defined lint */ --- 5,11 ---- #ifndef lint #ifndef NOID ! static char elsieid[] = "@(#)asctime.c 7.26"; #endif /* !defined NOID */ #endif /* !defined lint */ *************** *** 15,21 **** #include "tzfile.h" #if STRICTLY_STANDARD_ASCTIME ! #define ASCTIME_FMT "%.3s %.3s%3d %.2d:%.2d:%.2d %ld\n" #define ASCTIME_FMT_B ASCTIME_FMT #else /* !STRICTLY_STANDARD_ASCTIME */ /* --- 15,21 ---- #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 */ /* *************** *** 29,37 **** ** 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 ! ** spaces before the newline to get the newline in the traditional place. */ ! #define ASCTIME_FMT "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %-4ld\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 --- 29,37 ---- ** 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 *************** *** 38,44 **** ** 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 %ld\n" #endif /* !STRICTLY_STANDARD_ASCTIME */ #define STD_ASCTIME_BUF_SIZE 26 --- 38,44 ---- ** 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 *************** *** 74,80 **** }; register const char * wn; register const char * mn; ! long year; char result[MAX_ASCTIME_BUF_SIZE]; if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) --- 74,80 ---- }; 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) *************** *** 83,94 **** if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) mn = "???"; else mn = mon_name[timeptr->tm_mon]; - year = timeptr->tm_year + (long) TM_YEAR_BASE; /* ** We avoid using snprintf since it's not available on all systems. */ ! (void) sprintf(result, ! ((year >= -999 && year <= 9999) ? ASCTIME_FMT : ASCTIME_FMT_B), wn, mn, timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, --- 83,100 ---- if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) mn = "???"; else mn = mon_name[timeptr->tm_mon]; /* + ** 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); + /* ** 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, diff -c -r old/newctime.3 new/newctime.3 *** old/newctime.3 Mon Oct 11 14:44:49 2004 --- new/newctime.3 Thu Oct 14 13:00:48 2004 *************** *** 46,52 **** Thu Nov 24 18:22:48 1986\n\0 .br .ec ! Years requiring fewer than four characters are padded with trailing spaces. For years longer than four characters, the string is of the form .br .ce --- 46,52 ---- Thu Nov 24 18:22:48 1986\n\0 .br .ec ! Years requiring fewer than four characters are padded with leading zeroes. For years longer than four characters, the string is of the form .br .ce *************** *** 55,61 **** .ec .br with five spaces before the year. ! This unusual format is designed to make it less likely that older software that expects exactly 26 bytes of output will mistakenly output misleading values for out-of-range years. .PP --- 55,61 ---- .ec .br with five spaces before the year. ! These unusual formats are designed to make it less likely that older software that expects exactly 26 bytes of output will mistakenly output misleading values for out-of-range years. .PP *************** *** 65,71 **** return pointers to ``tm'' structures, described below. .I Localtime\^ corrects for the time zone and any time zone adjustments ! (such as Daylight Saving Time in the U.S.A.). After filling in the ``tm'' structure, .I localtime sets the --- 65,71 ---- return pointers to ``tm'' structures, described below. .I Localtime\^ corrects for the time zone and any time zone adjustments ! (such as Daylight Saving Time in the United States). After filling in the ``tm'' structure, .I localtime sets the *************** *** 234,237 **** Avoid using out-of-range values with .I mktime when setting up lunch with promptness sticklers in Riyadh. ! .\" @(#)newctime.3 7.16 --- 234,237 ---- Avoid using out-of-range values with .I mktime when setting up lunch with promptness sticklers in Riyadh. ! .\" @(#)newctime.3 7.17 diff -c -r old/strftime.c new/strftime.c *** old/strftime.c Mon Oct 11 14:46:51 2004 --- new/strftime.c Thu Oct 14 13:36:37 2004 *************** *** 1,12 **** - /* - ** XXX To do: figure out correct (as distinct from standard-mandated) - ** output for "two digits of year" and "century" formats when - ** the year is negative or less than 100. --ado, 2004-09-09 - */ - #ifndef lint #ifndef NOID ! static char elsieid[] = "@(#)strftime.c 7.67"; /* ** Based on the UCB version with the ID appearing below. ** This is ANSIish only when "multibyte character == plain character". --- 1,6 ---- #ifndef lint #ifndef NOID ! static char elsieid[] = "@(#)strftime.c 7.71"; /* ** Based on the UCB version with the ID appearing below. ** This is ANSIish only when "multibyte character == plain character". *************** *** 114,124 **** 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 *)); - extern char * tzname[]; #ifndef YEAR_2000_NAME --- 108,116 ---- static char * _add P((const char *, char *, const char *)); static char * _conv P((int, const char *, char *, const char *)); static char * _fmt P((const char *, const struct tm *, char *, const char *, int *)); + static char * _yconv P((int, int, int, int, char *, const char *)); extern char * tzname[]; #ifndef YEAR_2000_NAME *************** *** 125,131 **** #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" #endif /* !defined YEAR_2000_NAME */ - #define IN_NONE 0 #define IN_SOME 1 #define IN_THIS 2 --- 117,122 ---- *************** *** 217,225 **** ** something completely different. ** (ado, 1993-05-24) */ ! pt = _conv((int) ((t->tm_year + ! (long) TM_YEAR_BASE) / 100), ! "%02d", pt, ptlim); continue; case 'c': { --- 208,215 ---- ** something completely different. ** (ado, 1993-05-24) */ ! pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, ! pt, ptlim); continue; case 'c': { *************** *** 387,399 **** ** (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 ( ; ; ) { --- 377,390 ---- ** (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 ( ; ; ) { *************** *** 401,407 **** int bot; int top; ! len = isleap(year) ? DAYSPERLYEAR : DAYSPERNYEAR; /* --- 392,398 ---- int bot; int top; ! len = isleap_sum(year, base) ? DAYSPERLYEAR : DAYSPERNYEAR; /* *************** *** 420,426 **** top += DAYSPERWEEK; top += len; if (yday >= top) { ! ++year; w = 1; break; } --- 411,417 ---- top += DAYSPERWEEK; top += len; if (yday >= top) { ! ++base; w = 1; break; } *************** *** 429,436 **** DAYSPERWEEK); break; } ! --year; ! yday += isleap(year) ? DAYSPERLYEAR : DAYSPERNYEAR; } --- 420,427 ---- DAYSPERWEEK); break; } ! --base; ! yday += isleap_sum(year, base) ? DAYSPERLYEAR : DAYSPERNYEAR; } *************** *** 446,455 **** pt, ptlim); else if (*format == 'g') { *warnp = IN_ALL; ! pt = _conv(int(year % 100), ! "%02d", pt, ptlim); ! } else pt = _lconv(year, "%04ld", pt, ptlim); } continue; case 'v': --- 437,446 ---- 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; case 'v': *************** *** 486,498 **** continue; case 'y': *warnp = IN_ALL; ! pt = _conv((int) ((t->tm_year + ! (long) TM_YEAR_BASE) % 100), ! "%02d", pt, ptlim); continue; case 'Y': ! pt = _lconv(t->tm_year + (long) TM_YEAR_BASE, ! "%04ld", pt, ptlim); continue; case 'Z': #ifdef TM_ZONE --- 477,488 ---- 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 *************** *** 556,564 **** diff = -diff; } else sign = "+"; pt = _add(sign, pt, ptlim); ! diff /= 60; ! pt = _conv((diff/60)*100 + diff%60, ! "%04d", pt, ptlim); } continue; case '+': --- 546,555 ---- diff = -diff; } else sign = "+"; pt = _add(sign, pt, ptlim); ! diff /= SECSPERMIN; ! diff = (diff / MINSPERHOUR) * 100 + ! (diff % MINSPERHOUR); ! pt = _conv(diff, "%04d", pt, ptlim); } continue; case '+': *************** *** 596,614 **** } 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 * _add(str, pt, ptlim) const char * str; char * pt; --- 587,592 ---- *************** *** 619,624 **** --- 597,641 ---- return pt; } + /* + ** 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. + */ + + static char * + _yconv(a, b, convert_top, convert_yy, pt, ptlim) + const int a; + const int b; + const int convert_top; + const int convert_yy; + char * pt; + const char * const ptlim; + { + register int lead; + register int trail; + + #define DIVISOR 100 + lead = a / DIVISOR + b / DIVISOR; + trail = a % DIVISOR + b % DIVISOR; + while (trail < 0) { + trail += DIVISOR; + --lead; + } + if (lead < 0 && trail != 0) { + trail -= DIVISOR; + ++lead; + } + if (convert_top) + if (lead == 0 && trail < 0) + pt = _add("-0", pt, ptlim); + else pt = _conv(lead, "%02d", pt, ptlim); + if (convert_yy) + pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim); + } + #ifdef LOCALE_HOME static struct lc_time_T * _loc P((void)) diff -c -r old/tzfile.h new/tzfile.h *** old/tzfile.h Mon Oct 11 14:46:42 2004 --- new/tzfile.h Mon Oct 11 15:20:25 2004 *************** *** 21,27 **** #ifndef lint #ifndef NOID ! static char tzfilehid[] = "@(#)tzfile.h 7.14"; #endif /* !defined NOID */ #endif /* !defined lint */ --- 21,27 ---- #ifndef lint #ifndef NOID ! static char tzfilehid[] = "@(#)tzfile.h 7.16"; #endif /* !defined NOID */ #endif /* !defined lint */ *************** *** 156,167 **** #define EPOCH_YEAR 1970 #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 --- 156,176 ---- #define EPOCH_YEAR 1970 #define EPOCH_WDAY TM_THURSDAY + #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) + /* ! ** Since everything in isleap is modulo 400 (or a factor of 400), we know that ! ** isleap(y) == isleap(y % 400) ! ** and so ! ** isleap(a + b) == isleap((a + b) % 400) ! ** or ! ** isleap(a + b) == isleap(a % 400 + b % 400) ! ** This is true even if % means modulo rather than Fortran remainder ! ** (which is allowed by C89 but not C99). ! ** We use this to avoid addition overflow problems. */ ! #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) #ifndef USG diff -c -r old/zdump.c new/zdump.c *** old/zdump.c Mon Oct 11 14:46:47 2004 --- new/zdump.c Thu Oct 14 13:00:49 2004 *************** *** 1,4 **** ! static char elsieid[] = "@(#)zdump.c 7.40"; /* ** This code has been made independent of the rest of the time --- 1,4 ---- ! static char elsieid[] = "@(#)zdump.c 7.43"; /* ** This code has been made independent of the rest of the time *************** *** 61,69 **** #endif /* !defined DAYSPERNYEAR */ #ifndef isleap ! #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) #endif /* !defined isleap */ #if HAVE_GETTEXT #include "locale.h" /* for setlocale */ #include "libintl.h" --- 61,76 ---- #endif /* !defined DAYSPERNYEAR */ #ifndef isleap ! #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) #endif /* !defined isleap */ + #ifndef isleap_sum + /* + ** See tzfile.h for details on isleap_sum. + */ + #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) + #endif /* !defined isleap_sum */ + #if HAVE_GETTEXT #include "locale.h" /* for setlocale */ #include "libintl.h" *************** *** 321,327 **** return -delta(oldp, newp); result = 0; for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) ! result += DAYSPERNYEAR + isleap(tmy + (long) TM_YEAR_BASE); result += newp->tm_yday - oldp->tm_yday; result *= HOURSPERDAY; result += newp->tm_hour - oldp->tm_hour; --- 328,334 ---- return -delta(oldp, newp); result = 0; for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) ! result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE); result += newp->tm_yday - oldp->tm_yday; result *= HOURSPERDAY; result += newp->tm_hour - oldp->tm_hour; *************** *** 384,389 **** --- 391,398 ---- }; register const char * wn; register const char * mn; + int lead; + int trail; /* ** The packaged versions of localtime and gmtime never put out-of-range *************** *** 398,406 **** (int) (sizeof mon_name / sizeof mon_name[0])) mn = "???"; else mn = mon_name[timeptr->tm_mon]; ! (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d %ld", wn, mn, timeptr->tm_mday, timeptr->tm_hour, ! timeptr->tm_min, timeptr->tm_sec, ! timeptr->tm_year + (long) TM_YEAR_BASE); } --- 407,428 ---- (int) (sizeof mon_name / sizeof mon_name[0])) mn = "???"; else mn = mon_name[timeptr->tm_mon]; ! (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ", wn, mn, timeptr->tm_mday, timeptr->tm_hour, ! timeptr->tm_min, timeptr->tm_sec); ! #define DIVISOR 10 ! lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR; ! trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR; ! while (trail < 0) { ! trail += DIVISOR; ! --lead; ! } ! if (lead < 0 && trail != 0) { ! trail -= DIVISOR; ! ++lead; ! } ! if (lead == 0) ! (void) printf("%d", trail); ! else (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail)); }
"Olson, Arthur David (NIH/NCI)" <olsona@dc37a.nci.nih.gov> writes:
1. The asctime function punts to strftime to generate output representing the year
Unfortunately on further thought I noticed that the proposed asctime mishandles the STRICTLY_STANDARD_ASCTIME case. The standard requires a behavior as if %d were used, which means the year 999 should generate "999", not "0999". For strftime, the new _yconv is returns char *, but there's a missing "return pt;" before the end. The logic for "lead" and "trail" isn't right; for example, if a and b are both 99, then "trail" will be 198. These days my impression is that a single integer division is typically faster than looping once or twice, so I'll propose a fix based on division below. There's an "if (...) if (...) ... else ..." that some compilers (e.g., GCC) will complain about and will suggest adding brackets. Finally, if "top" and "yy" are renamed to "lead" and "trail", then "convert_top" and "convert_yy" should be renamed similarly. zdump.c's logic is a bit odd, e.g., it adds TM_YEAR_BASE % DIVISOR (which is zero) I guess for documentation, but it doesn't really handle the case of arbitrary TM_YEAR_BASE values correctly. As things stand the resulting "while" could be just an "if". Anyway, the patch proposed below makes zdump like strftime. The following proposed patch assumes the patch you just sent. =================================================================== RCS file: RCS/asctime.c,v retrieving revision 2004.3.1.2 retrieving revision 2004.3.0.4 diff -pc -r2004.3.1.2 -r2004.3.0.4 *** asctime.c 2004/10/14 18:03:04 2004.3.1.2 --- asctime.c 2004/10/14 20:14:54 2004.3.0.4 *************** static char elsieid[] = "@(#)asctime.c 7 *** 15,22 **** #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"; --- 15,22 ---- #include "tzfile.h" #if STRICTLY_STANDARD_ASCTIME ! #define ASCTIME_FMT "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n" ! #define ASCTIME_FMT_B "%.3s %.3s%3d %.2d:%.2d:%.2d %s\n" #else /* !STRICTLY_STANDARD_ASCTIME */ /* ** Some systems only handle "%.2d"; others only handle "%02d"; *************** static char elsieid[] = "@(#)asctime.c 7 *** 31,37 **** ** 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 --- 31,37 ---- ** 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 %-4d\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 *************** char * buf; *** 84,104 **** mn = "???"; else mn = mon_name[timeptr->tm_mon]; /* - ** 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); - /* ** 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; --- 84,114 ---- mn = "???"; else mn = mon_name[timeptr->tm_mon]; /* ** We avoid using snprintf since it's not available on all systems. */ ! if (-999 - TM_YEAR_BASE <= timeptr->tm_year ! && timeptr->tm_year <= 9999 - TM_YEAR_BASE) { ! (void) sprintf(result, ! ASCTIME_FMT, ! wn, mn, ! timeptr->tm_mday, timeptr->tm_hour, ! timeptr->tm_min, timeptr->tm_sec, ! 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". ! */ ! (void) strftime(year, sizeof year, "%Y", timeptr); ! (void) sprintf(result, ! 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; =================================================================== RCS file: RCS/strftime.c,v retrieving revision 2004.4.1.2 retrieving revision 2004.4.0.5 diff -pc -r2004.4.1.2 -r2004.4.0.5 *** strftime.c 2004/10/14 18:03:04 2004.4.1.2 --- strftime.c 2004/10/14 20:14:54 2004.4.0.5 *************** const char * const ptlim; *** 606,616 **** */ static char * ! _yconv(a, b, convert_top, convert_yy, pt, ptlim) const int a; const int b; ! const int convert_top; ! const int convert_yy; char * pt; const char * const ptlim; { --- 606,616 ---- */ static char * ! _yconv(a, b, convert_lead, convert_trail, pt, ptlim) const int a; const int b; ! const int convert_lead; ! const int convert_trail; char * pt; const char * const ptlim; { *************** const char * const ptlim; *** 618,639 **** register int trail; #define DIVISOR 100 - lead = a / DIVISOR + b / DIVISOR; trail = a % DIVISOR + b % DIVISOR; ! while (trail < 0) { trail += DIVISOR; --lead; ! } ! if (lead < 0 && trail != 0) { trail -= DIVISOR; ++lead; } ! if (convert_top) if (lead == 0 && trail < 0) pt = _add("-0", pt, ptlim); else pt = _conv(lead, "%02d", pt, ptlim); ! if (convert_yy) pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim); } #ifdef LOCALE_HOME --- 618,643 ---- register int trail; #define DIVISOR 100 trail = a % DIVISOR + b % DIVISOR; ! lead = a / DIVISOR + b / DIVISOR; ! lead += trail / DIVISOR; ! trail %= DIVISOR; ! if (trail < 0 && 0 < lead) { trail += DIVISOR; --lead; ! } else if (lead < 0 && 0 < trail) { trail -= DIVISOR; ++lead; } ! if (convert_lead) { if (lead == 0 && trail < 0) pt = _add("-0", pt, ptlim); else pt = _conv(lead, "%02d", pt, ptlim); ! } ! if (convert_trail) pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim); + + return pt; } #ifdef LOCALE_HOME =================================================================== RCS file: RCS/zdump.c,v retrieving revision 2004.4.1.2 retrieving revision 2004.4.0.5 diff -pc -r2004.4.1.2 -r2004.4.0.5 *** zdump.c 2004/10/14 18:03:04 2004.4.1.2 --- zdump.c 2004/10/14 20:14:54 2004.4.0.5 *************** register const struct tm * timeptr; *** 413,424 **** timeptr->tm_min, timeptr->tm_sec); #define DIVISOR 10 lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR; ! trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR; ! while (trail < 0) { trail += DIVISOR; --lead; ! } ! if (lead < 0 && trail != 0) { trail -= DIVISOR; ++lead; } --- 413,423 ---- timeptr->tm_min, timeptr->tm_sec); #define DIVISOR 10 lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR; ! trail = timeptr->tm_year % DIVISOR; ! if (trail < 0 && 0 < lead) { trail += DIVISOR; --lead; ! } else if (lead < 0 && 0 < trail) { trail -= DIVISOR; ++lead; }
participants (2)
-
Olson, Arthur David (NIH/NCI) -
Paul Eggert