Strftime's %C and %y formats versus wide-ranging tm_year valu es

Paul Eggert eggert at CS.UCLA.EDU
Mon Sep 27 05:07:02 UTC 2004


"Olson, Arthur David (NIH/NCI)" <olsona at dc37a.nci.nih.gov> writes:

> Absent any objections, ...

I found a bug on 32-bit hosts: it misformats %Y, %y, %C, etc., when
t->tm_year is close to INT_MAX.  This is because tm_year + 1900
t->overflows.  In the common 32-bit case where 'long' is the
same width as 'int', converting to 'long' first doesn't help.

This bug was present in the old version too, but we might as well fix
it while we're in the neighborhood.  I'll send a proposed fix in my
next email, but first I'll give an illustration of the bug.  The
following test program (derived from the program you emailed) will
output lines that look like this (modulo white space):

  Y-1900      %C         %Y           %y  %G           %g
  ...
  2147483647  -21474817  -2147481749  49  -2147481749  49
  2147483646  -21474817  -2147481750  50  -2147481750  50
  2147483645  -21474817  -2147481751  51  -2147481751  51
  2147481748  -21474836  -2147483648  48  -2147483648  48

instead of the correct answers, which look like this:

  Y-1900      %C         %Y           %y  %G           %g
  ...
  2147483647  21474855   2147485547   47  2147485547   47
  2147483646  21474855   2147485546   46  2147485546   46
  2147483645  21474855   2147485545   45  2147485545   45
  2147481748  21474836   2147483648   48  2147483648   48


#include <limits.h>
#include <stdio.h>
#include <time.h>

static int years[] = {
  10001 - 1900, 10000 - 1900, 9999 - 1900,
  1001 - 1900, 1000 - 1900, 999 - 1900,
  101 - 1900, 100 - 1900, 99 - 1900,
  11 - 1900, 10 - 1900, 9 - 1900,
  1 - 1900, 0 - 1900, -1 - 1900,
  -9 - 1900, -10 - 1900, -11 - 1900,
  -99 - 1900, -100 - 1900, -101 - 1900,
  -999 - 1900, -1000 - 1900, -1001 - 1900,
  -9999 - 1900, -10000 - 1900, -10001 - 1900,
  INT_MAX, INT_MAX - 1, INT_MAX - 2,
  INT_MAX - 1900 + 1, INT_MAX - 1900, INT_MAX - 1 - 1900,
  INT_MIN + 2, INT_MIN + 1, INT_MIN
};

#define NVALUES ((sizeof years) / (sizeof years[0]))

static char *formats[] = { "%C", "%Y", "%y", "%G", "%g" };

#define NFORMATS        ((sizeof formats) / (sizeof formats[0]))

#define TM_BASE_YEAR    1900

main ()
{
  static struct tm tm;
  int i;
  int j;
  char buf[1024];

  tm.tm_mon = 0;		/* months since January => January */
  tm.tm_mday = 8;		/* day of the month */
  tm.tm_yday = 7;		/* days since January 1 */
  (void) printf ("Y-1900");
  for (j = 0; j < NFORMATS; ++j)
    {
      (void) printf ("\t%s", formats[j]);
    }
  (void) printf ("\n");
  for (i = 0; i < NVALUES; ++i)
    {
      (void) printf ("%d", years[i]);
      tm.tm_year = years[i];
      for (j = 0; j < NFORMATS; ++j)
	{
	  (void) strftime (buf, sizeof buf, formats[j], &tm);
	  (void) printf ("\t%s", buf);
	}
      (void) printf ("\n");
    }
  return 0;
}



More information about the tz mailing list