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

Olson, Arthur David (NIH/NCI) olsona at dc37a.nci.nih.gov
Thu Sep 23 19:39:37 UTC 2004


At the end of this message you'll find proposed changes to strftime.c to do
the right thing for wide-ranging years.
I compiled it with this quick-and-dirty source code...

	#include "stdio.h"
	#include "sys/types.h"
	#include "time.h"

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

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

	static char *	formats[] = { "%C", "%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("YEAR");
		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] - TM_BASE_YEAR;
			for (j = 0; j < NFORMATS; ++j) {
				(void) strftime(buf, sizeof buf, formats[j],
&tm);
				(void) printf("\t%s", buf);
			}
			(void) printf("\n");
		}
		return 0;
	}

...and got these results...

	YEAR	%C	%y	%G	%g
	10001	100	01	10001	01
	10000	100	00	10000	00
	9999	99	99	9999	99
	1001	10	01	1001	01
	1000	10	00	1000	00
	999	09	99	0999	99
	101	01	01	0101	01
	100	01	00	0100	00
	99	00	99	0099	99
	11	00	11	0011	11
	10	00	10	0010	10
	9	00	09	0009	09
	1	00	01	0001	01
	0	00	00	0000	00
	-1	-0	01	-001	01
	-9	-0	09	-009	09
	-10	-0	10	-010	10
	-11	-0	11	-011	11
	-99	-0	99	-099	99
	-100	-1	00	-100	00
	-101	-1	01	-101	01
	-999	-9	99	-999	99
	-1000	-10	00	-1000	00
	-1001	-10	01	-1001	01
	-9999	-99	99	-9999	99
	-10000	-100	00	-10000	00
	-10001	-100	01	-10001	01

Absent any objections, I'll promulgate these changes next week (along with
Paul Eggert's revised update to tz-link.htm).

					--ado

------- strftime.c -------
*** /tmp/geta12949	Thu Sep 23 15:27:03 2004
--- /tmp/getb12949	Thu Sep 23 15:27:03 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.69";
  /*
  ** Based on the UCB version with the ID appearing below.
  ** This is ANSIish only when "multibyte character == plain character".
***************
*** 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':
  				{
--- 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':
  				{
***************
*** 445,453 ****
  						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);
  				}
--- 450,464 ----
  						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);
  				}
***************
*** 486,494 ****
  				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,
--- 497,512 ----
  				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,
***************
*** 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 '+':
--- 574,583 ----
  					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 '+':



More information about the tz mailing list