« Social media spam -- the pain begins | Main | Yes, hiring again. »

Interesting HP-UX time bug

Thanks go to my good friend Mark Shoulson for help in discovering this.

So we're looking at some very old legacy code that does some date calculations in a particularly ... stunning ... fashion and we wanted to replace it with native Unix time functions for calculations.

So Mark and I wrote a test driver program to make sure that his functions would be bug-for-bug compatible with our current codebase, and instead uncovered the following interesting fact on HP-UX 11.3: one day after January 1, 2100 is January 1, 2100. (I guess Groundhog Day got moved that year.)

So we tried with this code:


#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

int
main(argc, argv)
     int argc;
     char **argv;
{
  struct tm thedate;
  time_t timet;
  int n;
  char outdate[11];
  char conv[11];
  if (argc<3) {
    printf("Usage: %s MM-DD-YYYY N\n", argv[0]);
    exit(1);
  }
  if (NULL==strptime(argv[1], "%m-%d-%Y", &thedate)) {
    printf("strptime(%s, \"%%m-%%d-%%Y\",...) failed.\n", argv[1]);
    exit(1);
  }
  n=atoi(argv[2]);
  thedate.tm_mday+=n;
  thedate.tm_hour=12; thedate.tm_min=thedate.tm_sec=0;
  timet=mktime(&thedate);
        printf("(mktime returned %ld)\n",(long)timet);
  strftime(outdate,11,"%m-%d-%Y",&thedate);

  strcpy(conv,argv[1]);
  printf("%s + %d days:\n\tstrptime computes:\t%s\n",
         argv[1], n, outdate);
}

(We set the hour to be 12n as opposed to midnight to avoid a timezone issue, we're in EST5EDT not GMT.)

Well, we get, on Linux:


[jbaltz@linux ~]$ ./FDATE 08-23-2012 1
(mktime returned 1345827600)
08-23-2012 + 1 days:
        strptime computes:      08-24-2012
and on HP-UX

jbaltz@hp-ux [~]> ./FDATE 08-23-2012 1
(mktime returned 1345827600)
08-23-2012 + 1 days:
        strptime computes:      08-24-2012
Not too shabby: tomorrow is indeed the twenty-fourth of August, 2012.

Let's try around the troublesome 32-bit overflow:


[jbaltz@linux ~]$ ./FDATE 01-18-2038 2
(mktime returned 2147619600)
01-18-2038 + 2 days:
        strptime computes:      01-20-2038
and

jbaltz@hp-ux [~]> ./FDATE 01-18-2038 2
(mktime returned 2147619600)
01-18-2038 + 2 days:
        strptime computes:      01-20-2038
OK, let's go for the gold: the year 2100:

[jbaltz@linux ~]$ ./FDATE 01-01-2100 1
(mktime returned 4102592400)
01-01-2100 + 1 days:
        strptime computes:      01-02-2100
but on HP-UX:


jbaltz@hp-ux [~]> ./FDATE 01-01-2100 1
(mktime returned 4102506000)
01-01-2100 + 1 days:
        strptime computes:      01-01-2100
jbaltz@hp-ux [~]> ./FDATE 12-31-2099 2
(mktime returned 4102506000)
12-31-2099 + 2 days:
        strptime computes:      01-01-2100
jbaltz@hp-ux[~]> ./FDATE 12-31-2099 3
(mktime returned 4102592400)
12-31-2099 + 3 days:
        strptime computes:      01-02-2100
jbaltz@hp-ux [~]> ./FDATE 01-01-2100 2
(mktime returned 4102592400)
01-01-2100 + 2 days:
        strptime computes:      01-02-2100

oopsie!

Yes, we're filing a bug report. Hopefully, if this hasn't been patched already, it will be before 2100.

TrackBack

TrackBack URL for this entry:
http://www.jbaltz.com/mt/mt-tb.cgi/117

Comments

Just to be specific, we've pinned it down to mktime(). If you have a struct tm in which tm_year=200, tm_month=0, tm_mday=2, i.e. 1 January 2100 (remember, months are 0-indexed, years are 1900-indexed, and days are 1-indexed), and innocuous values for time, and you run it through mktime, the struct you get back has the tm_mday set back to 1. In fact, for any positive tm_mday throughout the whole year 2100, mktime will always subtract one from the mday (in addition to renormalizing, if necessary). I haven't checked negatives.

In 2200, January 1 is okay, but February 1 isn't. Only dates later than January 2 are bad. In 2300, we're good all the way up through January 4. 2400 is a leap year, and is all Just Fine. Non-century years look okay, though we haven't tested them all.

This is 64-bit HPUX, so it is supposed to be good through 31 December 9999 (no Y2.37k bug here).

Once again, for tl;dr crowd: mktime subtracts one from the tm_mday.

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)