Strftime's %C and %y formats versus wide-ranging tm_year valu es
Olson, Arthur David (NIH/NCI)
olsona at dc37a.nci.nih.gov
Tue Oct 5 15:17:45 UTC 2004
Here's the latest try at coping with wide-ranging tm_year values.
This is based on the proposals circulated by Paul Eggert.
I've tried to deal with the challenges of systems where the compiler's %
operator doesn't do things the C99 way.
I've also tried to deal with systems where sizeof (int) == sizeof (long) and
the "long long" type is unavailable;
Improvidently, this means doing double math in some cases.
Note that these changes are relative to the stuff that's currently in
ftp://elsie.nci.nih.gov
--ado
diff -c -r old/code/tzfile.h new/code/tzfile.h
*** old/code/tzfile.h Wed Aug 11 11:59:05 2004
--- new/code/tzfile.h Tue Oct 5 10:17:24 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.15";
#endif /* !defined NOID */
#endif /* !defined lint */
***************
*** 157,168 ****
#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,184 ----
#define EPOCH_WDAY TM_THURSDAY
/*
! ** Given an integral argument (a) and a positive integral argument (b),
! ** return a % b per C99.
*/
+ #define C99IPMOD(a, b) ((-1 % 2 < 0 || (a) >= 0) ? \
+ ((a) % (b)) : ((a) % (b) - (b)))
+
#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)
+ ** (at least under the C99 definition of %).
+ ** We use this to avoid addition overflow problems.
+ */
+
+ #define isleap_sum(a, b) isleap(C99IPMOD((a), 400) + C99IPMOD((b),
400))
+
#ifndef USG
/*
diff -c -r old/code/asctime.c new/code/asctime.c
*** old/code/asctime.c Wed Aug 11 11:59:06 2004
--- new/code/asctime.c Tue Oct 5 10:27:18 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.23";
#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 %.0lf\n"
#define ASCTIME_FMT_B ASCTIME_FMT
#else /* !STRICTLY_STANDARD_ASCTIME */
/*
***************
*** 31,37 ****
** 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
--- 31,37 ----
** 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 %-4.0lf\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 %.0lf\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;
! double year;
char result[MAX_ASCTIME_BUF_SIZE];
if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
***************
*** 83,89 ****
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.
*/
--- 83,89 ----
if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
mn = "???";
else mn = mon_name[timeptr->tm_mon];
! year = (double) timeptr->tm_year + (double) TM_YEAR_BASE;
/*
** We avoid using snprintf since it's not available on all systems.
*/
diff -c -r old/code/strftime.c new/code/strftime.c
*** old/code/strftime.c Thu Sep 9 11:48:53 2004
--- new/code/strftime.c Tue Oct 5 10:20:02 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.70";
/*
** 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;
}
***************
*** 444,455 ****
if (*format == 'V')
pt = _conv(w, "%02d",
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':
--- 435,446 ----
if (*format == 'V')
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;
case 'v':
***************
*** 484,499 ****
*warnp = warn2;
}
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
if (t->TM_ZONE != NULL)
--- 475,489 ----
*warnp = warn2;
}
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
if (t->TM_ZONE != NULL)
***************
*** 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,632 ----
return pt;
}
+ 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;
+ {
+ char buf[INT_STRLEN_MAXIMUM(int) + 2];
+ int i;
+ char * cp;
+
+ if (!convert_top && !convert_yy)
+ return pt;
+ cp = buf;
+ i = a + b;
+ if ((i > a) == (b > 0))
+ (void) sprintf(cp, "%04d", i);
+ else if (sizeof (long) > sizeof (int))
+ (void) sprintf(cp, "%04ld", (long) a + (long) b);
+ else (void) sprintf(cp, "%04.0lf", (double) a + (double) b);
+ i = strlen(cp) - 2;
+ if (!convert_top)
+ cp += i;
+ else if (!convert_yy)
+ cp[i] = '\0';
+ return _add(cp, pt, ptlim);
+ }
+
#ifdef LOCALE_HOME
static struct lc_time_T *
_loc P((void))
diff -c -r old/code/zdump.c new/code/zdump.c
*** old/code/zdump.c Mon Sep 6 16:00:46 2004
--- new/code/zdump.c Tue Oct 5 10:17:24 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.41";
/*
** This code has been made independent of the rest of the time
***************
*** 60,69 ****
#define DAYSPERNYEAR 365
#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"
--- 60,94 ----
#define DAYSPERNYEAR 365
#endif /* !defined DAYSPERNYEAR */
+ #ifndef C99IPMOD
+ /*
+ ** Given an integral argument (a) and a positive integral argument (b),
+ ** return a % b per C99.
+ */
+
+ #define C99IPMOD(a, b) ((-1 % 2 < 0 || (a) >= 0) ? \
+ ((a) % (b)) : ((a) % (b) - (b)))
+ #endif /* !defined C99IPMOD */
+
#ifndef isleap
! #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) ==
0))
#endif /* !defined isleap */
+ #ifndef isleap_sum
+ /*
+ ** 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)
+ ** (at least under the C99 definition of %).
+ ** We use this to avoid addition overflow problems.
+ */
+
+ #define isleap_sum(a, b) isleap(C99IPMOD((a), 400) + C99IPMOD((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;
--- 346,352 ----
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;
***************
*** 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);
}
--- 423,431 ----
(int) (sizeof mon_name / sizeof mon_name[0]))
mn = "???";
else mn = mon_name[timeptr->tm_mon];
! (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d %.0lf",
wn, mn,
timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec,
! (double) timeptr->tm_year + (double) TM_YEAR_BASE);
}
More information about the tz
mailing list