strftime %y and negative years
Paul Eggert
eggert at CS.UCLA.EDU
Thu Aug 19 22:57:13 UTC 2004
"Olson, Arthur David (NIH/NCI)" <olsona at dc37a.nci.nih.gov> writes:
> ...the suggested change to strftime is trying to get %y to
> produce the "year within the century" as output.
Yes, that's correct: it's trying to produce the year modulo 100.
> But the latest IEEE Std 1003.1 calls for...
>
> %y Replaced by the last two digits of the year as a decimal
> number [00,99].
Read literally, this would have undefined behavior for the years -9
through 9 (since they don't have two digits), and would generate "10"
for the year -10, and so forth, which obviously disagrees with the
proposed "year modulo 100" semantics.
However, I just checked 3 implementations and found that nobody obeys
the literal behavior for years before -9, that there's disagreement
about negative single-digit years, and that at least one traditional
Unix implementation mishandles years before 1900 (not too surprising,
since the Unix Version 7 ctime allowed only years in the range
1900..2099).
Here's what I found.
year (i.e., glibc 2.2.5 OpenBSD 3.4 Solaris 9
tm_year-1900) (Debian patch 112874-29
2.2.5-11.5) (64-bit sparc)
-101 99 -1 0/
-100 00 00 00
-99 01 -99 '' (i.e., two apostrophes)
...
-2 98 -2 0.
-1 99 -1 0/
0 00 00 00
1 01 01 ''
...
9 09 09 '/
10 10 10 '0
11 11 11 ('
...
99 99 99 0/
100 00 00 00
101 01 01 ''
...
1899 99 99 0/
1900 00 00 00
...
The implementations agreed for years after 1899, until they
got to the year 2**31:
2**31 - 1 47 47 47
2**31 48 -48 48
2**31 + 1 49 -47 49
2**31 + 2 50 -46 50
....
2**31 + 1897 45 -51 45
2**31 + 1898 46 -50 46
2**31 + 1899 47 -49 47
So it appears that, in practice, the behavior of strftime %y is
undefined when tm_year is negative, or when tm_year+1900 exceeds
INT_MAX.
I don't know whether the standards committee would consider all this to be
a bug in the standard or in the implementations, but here's what I think.
The Solaris behavior is clearly buggy for years before 1900.
The OpenBSD behavior is clearly buggy for years after 2**31 - 1.
For years before 0, I suppose that it's debatable between OpenBSD 3.4
and glibc 2.2.5. However, I'd say that the glibc 2.2.5 behavior is
cleaner, since it's more regular, it always outputs two bytes, and
it doesn't output "-".
PS. Disclaimer: I wrote that part of glibc 2.2.5 so my opinion is
hardly impartial.
PPS. Ironic, isn't it? The consensus is that new code shouldn't use
ctime, as it's obsolete and is undefined for years out of the
traditional range, and that new code should use strftime. But in
practice, strftime has problems too.
PPPS. I'll CC: this to the tz mailing list to see if anybody else has
experiences with other implementations in this area, or strong
opinions on the subject.
More information about the tz
mailing list