Subject: lib/5161: strptime(3) gets %j, %m, and %S conversions off by one and breaks %C
To: None <gnats-bugs@gnats.netbsd.org>
From: None <woods@most.weird.com>
List: netbsd-bugs
Date: 03/14/1998 22:06:19
>Number: 5161
>Category: lib
>Synopsis: strptime(3) gets %j, %m, and %S conversions off by one and breaks %C
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: lib-bug-people (Library Bug People)
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sat Mar 14 19:20:00 1998
>Last-Modified:
>Originator: Greg A. Woods
>Organization:
Planix, Inc.; Toronto, Ontario; Canada
>Release: NetBSD-1998/03/03 (Tue Mar 3 07:52:24 EST 1998)
>Environment:
System: NetBSD most 1.3 NetBSD 1.3 (GENERIC_SCSI3) #0: Thu Jan 1 19:03:39 MET 1998 pk@flambard:/usr/src1/sys/arch/sparc/compile/GENERIC_SCSI3 sparc
>Description:
The code in strptime(3) does not take into account the valid
ranges for various fields in "struct tm", in particular tm_sec,
tm_mon, and tm_yday when doing the conversions for '%S', '%m',
and '%j' repsectively.
The '%C' obliterates the value of tm_year from '%y' with the
value of only the century, and conversely '%y' obliterates the
value from '%C'. Since these conversions are only valid in
conjunction with each other their effect must be additive. I
have no idea what XPG-4 has to say about this, but if it doesn't
agree with me then it's clearly broken! ;-)
Unfortunately the regression test for strptime(3) is also
incorrect and incomplete.
Lastly there are some minor errors in the manual page, and some
silly "KNF"-like fixes that should be done.
>How-To-Repeat:
Convert various dates from ASCII through struct tm and back to
ASCII again using strptime(3) and strftime(3) and observe the
differences.
>Fix:
Apply the following patches. Note that I've corrected the
regression test, but have not made it more complete. (The
second and third hunks of the man-page diff contain the critical
updates.)
Index: lib/libc/time/strptime.c
===================================================================
RCS file: /cvs/NetBSD/src/lib/libc/time/strptime.c,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 strptime.c
*** strptime.c 1998/02/20 00:32:20 1.1.1.1
--- strptime.c 1998/03/15 02:41:32
***************
*** 216,222 ****
if (!(_conv_num(&bp, &i, 0, 99)))
return (0);
! tm->tm_year = i * 100;
break;
case 'd': /* The day of month. */
--- 216,222 ----
if (!(_conv_num(&bp, &i, 0, 99)))
return (0);
! tm->tm_year += i * 100;
break;
case 'd': /* The day of month. */
***************
*** 246,253 ****
case 'j': /* The day of year. */
_LEGAL_ALT(0);
! if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
return (0);
break;
case 'M': /* The minute. */
--- 246,254 ----
case 'j': /* The day of year. */
_LEGAL_ALT(0);
! if (!(_conv_num(&bp, &i, 1, 366)))
return (0);
+ tm->tm_yday = i - 1;
break;
case 'M': /* The minute. */
***************
*** 258,265 ****
case 'm': /* The month. */
_LEGAL_ALT(_ALT_O);
! if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
return (0);
break;
case 'p': /* The locale's equivalent of AM/PM. */
--- 259,267 ----
case 'm': /* The month. */
_LEGAL_ALT(_ALT_O);
! if (!(_conv_num(&bp, &i, 1, 12)))
return (0);
+ tm->tm_mon = i - 1;
break;
case 'p': /* The locale's equivalent of AM/PM. */
***************
*** 290,297 ****
case 'S': /* The seconds. */
_LEGAL_ALT(_ALT_O);
! if (!(_conv_num(&bp, &tm->tm_sec, 1, 61)))
return (0);
break;
case 'U': /* The week of year, beginning on sunday. */
--- 292,300 ----
case 'S': /* The seconds. */
_LEGAL_ALT(_ALT_O);
! if (!(_conv_num(&bp, &i, 1, 61)))
return (0);
+ tm->tm_sec = i - 1;
break;
case 'U': /* The week of year, beginning on sunday. */
***************
*** 327,335 ****
return (0);
if (i <= 68)
! tm->tm_year = i + 2000 - TM_YEAR_BASE;
else
! tm->tm_year = i + 1900 - TM_YEAR_BASE;
break;
/*
--- 330,338 ----
return (0);
if (i <= 68)
! tm->tm_year += i + 2000 - TM_YEAR_BASE;
else
! tm->tm_year += i + 1900 - TM_YEAR_BASE;
break;
/*
Index: lib/libc/time/strptime.3
===================================================================
RCS file: /cvs/NetBSD/src/lib/libc/time/strptime.3,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 strptime.3
*** strptime.3 1998/02/20 00:32:20 1.1.1.1
--- strptime.3 1998/03/15 03:00:41
***************
*** 57,71 ****
.Pp
The
.Fa format
! string consists of zero or more conversion specifications and
! ordinary characters. All ordinary characters are copied directly into
! the buffer. A conversion specification consists of a percent sign `%'
! followed by one or two conversion characters which specify the replacement
! required. There must be white-space or other non-alphanumeric characters
! between any two conversion specifications.
.Pp
The LC_TIME category defines the locale values for the conversion
! specifications. The following conversion specifications are supported:
.Bl -tag -width "xxxx"
.It Cm \&%a
the day of week, using the locale's weekday names;
--- 57,71 ----
.Pp
The
.Fa format
! string consists of zero or more conversion specifications and ordinary
! characters. All ordinary characters are copied directly into the
! buffer. A conversion specification consists of a percent sign `%'
! followed by one or two conversion characters which specify the
! replacement required. There must be white-space or other
! non-alphanumeric characters between any two conversion specifications.
.Pp
The LC_TIME category defines the locale values for the conversion
! specifications. The following conversion specifications are supported:
.Bl -tag -width "xxxx"
.It Cm \&%a
the day of week, using the locale's weekday names;
***************
*** 82,92 ****
.It Cm \&%c
the date and time, using the locale's date and time format.
.It Cm \&%C
! the century number [0,99];
! leading zeros are permitted but not required.
.It Cm \&%d
the day of month [1,31];
! leading zeros are permitted but required.
.It Cm \&%D
the date as %m/%d/%y.
.It Cm \&%e
--- 82,93 ----
.It Cm \&%c
the date and time, using the locale's date and time format.
.It Cm \&%C
! the century number [0,99]; leading zeros are permitted but not required.
! Note that the converted value is added to the current value of the
! ``tm_year'' field (in order that the "\&%y" conversion be useful).
.It Cm \&%d
the day of month [1,31];
! leading zeros are permitted but not required.
.It Cm \&%D
the date as %m/%d/%y.
.It Cm \&%e
***************
*** 150,160 ****
the time, using the locale's time format.
.It Cm \&%y
the year within the 20th century [69,99] or the 21st century [0,68];
! leading zeros are permitted but not required.
.It Cm \&%Y
the year, including the century (i.e., 1996).
.It Cm \&%%
! A `%' is written. No argument is converted.
.El
.Ss Modified conversion specifications
For compatibility, certain conversion specifications can be modified
--- 151,163 ----
the time, using the locale's time format.
.It Cm \&%y
the year within the 20th century [69,99] or the 21st century [0,68];
! leading zeros are permitted but not required. Note that the converted
! value is added to the current value of the ``tm_year'' field (in order
! that the "\&%C" conversion be useful).
.It Cm \&%Y
the year, including the century (i.e., 1996).
.It Cm \&%%
! A `%' is written. No argument is converted.
.El
.Ss Modified conversion specifications
For compatibility, certain conversion specifications can be modified
***************
*** 162,177 ****
.Cm E
and
.Cm O
! modifier characters to indicate that an alternative format or specification
! should be used rather than the one normally used by the unmodified
! conversion specification. As there are currently neither alternative formats
! nor specifications supported by the system, the behavior will be as if the
! unmodified conversion specification were used.
.Sh RETURN VALUES
If successful, the
.Nm
function returns a pointer to the character following the last character
! parsed. Otherwise, a null pointer is returned.
.Sh SEE ALSO
.Xr strftime 3
.Sh STANDARDS
--- 165,181 ----
.Cm E
and
.Cm O
! modifier characters to indicate that an alternative format or
! specification should be used rather than the one normally used by the
! unmodified conversion specification. As there are currently neither
! alternative formats nor specifications supported by the system, the
! behavior will be as if the unmodified conversion specification were
! used.
.Sh RETURN VALUES
If successful, the
.Nm
function returns a pointer to the character following the last character
! parsed. Otherwise, a null pointer is returned.
.Sh SEE ALSO
.Xr strftime 3
.Sh STANDARDS
Index: regress/lib/libc/time/strptime/expected
===================================================================
RCS file: /cvs/NetBSD/src/regress/lib/libc/time/strptime/expected,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 expected
*** expected 1998/02/20 00:33:23 1.1.1.1
--- expected 1998/03/15 00:09:11
***************
*** 1,14 ****
--- ctime --- 1
succeeded
! 46 27 23 20 0 98 2 -1
--- ctime --- 2
succeeded
! 46 27 23 20 0 98 2 -1
--- ctime --- 3
succeeded
! 46 27 23 20 0 98 2 -1
--- %a --- 1
succeeded
--- 1,14 ----
--- ctime --- 1
succeeded
! 45 27 23 20 0 98 2 -1
--- ctime --- 2
succeeded
! 45 27 23 20 0 98 2 -1
--- ctime --- 3
succeeded
! 45 27 23 20 0 98 2 -1
--- %a --- 1
succeeded
>Audit-Trail:
>Unformatted: