Year 2000 problems in C

Introduction

Year 2000 problems occur because code has been written that only uses the last 2 digits of the year.

Problems can arise from two main sources:

The representation of integers in C, using two's compliment notation (or even ones compliment), means that datatypes are likely to be less of an issue than in languages that have the ability to use BCD, like Cobol.

Solving Year 2000 problems is a three stage process:

The job of an automatic checking tool is to scan all of the source code looking for potential problems.

I/O problems

Writing

 printf("... %2d ...\n", x.tm_year);

Here the width is a mimimum limit, not a maximum. The tm_year field of a tm struct is the number of years since 1900. Once the year 2000 arrives the printed form of this value will contain 3 digits, not 2. All three digits will be printed, changing the appearance of the output.

Reading

 scanf("... %2d ...", &x);

For the scanf family of functions a width specifies the maximum number of characters to be read for that value. So in the above case a maximum of 2 characters of input will be used to form the value assigned to x.

Strings

Each character making up a string can hold one digit. Strings are thus a prime source for problems that relate to using two digits to represent a year. We would like to limit the number of false warnings generated. So we don't necessarily want to flag all strings that contain numeric values. Context is used to help focus down the warnings to specific cases.

Two digit comparisons

Calling a string comparison library function with one of the arguments being a two digit string, is cause for concern.

 strcmp(x, "89");

Library functions

The ISO C standard defines the contents of a set of time related functions, declared in time.h. Most of these functions return numeric values, but a few return strings The asctime function is defined in the C standard. It returns the time and date as an ASCII string. Code that calls this system service and then indexes into the returned string to get the last two digits of the year obviously needs changing.

str=asctime(...)
date=str[offset of last 2 year digits];

The %y specifier to strftime returns the year without century information. The %Y specifier includes century information.

Month names

Code that manipulates dates is likely to contain the names of months. Strings containing the month names, or common abbreviations of these names should be flagged.

Date like format

 "dd/mm/yy"

The above is an example of a common shorthand for writing dates (the relative ordering of the day, month and year does vary across cultures). A pattern of this form occurring, in a string, is probably worth flagging.

Good programmers give meaningful names to identifiers

Identifiers with the characters 'year' in them are probably worth visual inspection. Of course other, common abbreviations can also be checked.

Using OSPC to flag these constructs

OSPC (Open Systems Portability Checker) can be configured to flag many of the above constructs.

Identifier checks

It is a very simple matter to add checks based on identifier name using the existing API testing machinery.

Create a text file containing the specification:

 #reserved
 .*year.*	5000	any
 .*YEAR.*	5000	any
 .*yr.*		5000	any
 .*YR.*		5000	any
 #end

Process this file with the tool iddb, producing an output file called year.id, say. Add the option -chk year.id to your OSPC configuration file (called .mccrc in your home directory). Henceforth any identifier containing the character sequences 'year', 'YEAR', 'yr' or 'YR' will be flagged with error 5000. Follow the instructions in the OSPC on how to create an error file, enabling a text message to be given.

The same technique will also support the flagging of field names, such as tm_year.

String contents

As well as matching identifier names it is possible to match the contents of strings.

Create a text file containing the specification (the original use for this information was detecting file path names in strings, hence the name):

 #path
 *
 * Match two consecutive digits
 *
 :d:d	5001
 *
 * Match %2d, %2ld, other combinations to be added
 %2d	5002
 %2ld	5003
 #end
Once again this file needs to be processed by iddb and made visible to OSPC using the -chk option.

X/Open library functions

getdate()

struct tm *getdate(const char *string);

The X/Open CAE Specification, System Interfaces and Headers Issue 4, Version 2 (September 1994), in the entry for getdate() on page 231, states the following with respect to the format code %y:

        "%y     year within century (00-99)"
Suppose there is a line of the form "%m/%d/%y" in the template file, and a call is made:
  struct tm * p_tm;
  p_tm = getdate( "11/22/02" );
It is not specified what would be represented in the struct tm for the century. This could be interpreted as either 11/22/1902 or 11/22/2002 since the implementation does not know which "century" this is to refer to.

Advice to programmers: Use the %Y field descriptor which defines year as a four digit field (ccyy).

Future directions: A future revision of the X/Open CAE Specification will add the %C specifier to the interface to denote the century.

strptime()

char *strptime(const char *buf, const char *format, struct tm *tm);

The X/Open CAE Specification, System Interfaces and Headers Issue 4, Version 2 (September 1994), in the entry for strptime() on page 615, states the following with respect to the format code %y:

        "%y     is the year within century [00,99]; leading zeros are
                permitted but not required"
Suppose that the following call is made:
 struct tm thetime;
 strptime( "11/22/02", "%m/%d/%y", &thetime );
It is not specified what would be represented in the struct tm for the century. This could be interpreted as either 11/22/1902 or 11/22/2002 since the implementation does not know which "century" this is to refer to.

Advice to programmers: Use the %Y field descriptor which defines year as a four digit field (ccyy).


Home

© Copyright 1996,97,2000. Knowledge Software Ltd. All rights reserved;
Last modified 13 Jul 2000