From: msb@sq.com (Mark Brader)
Subject: Re: routine for yesterday's date
Message-ID: your message <9603300430.AA19052@sqrex.sq.com>
Date: Fri, 29 Mar 96 23:30:02 EST

Bruce Coghill writes:
> In my C manual the time.h and time functions are described
> and how I can pull off each element in the structure (like year,
> month, day, day-of-the-year), but it isn't clear to me how to subtract
> a day.

This is question 13.14 on the FAQ list, but I'd like to expand a bit on what it says there.

Bruce, you have gotten as far as calling time() and localtime(). The next thing is to use the normalizing feature of mktime(). Suppose that you have a variable of type struct tm called x, and you've just put the current time into it using localtime(). Now you basically just want to do something like this:

	x.tm_day--;      	/* back 1 day */
	(void) mktime (&x);	/* normalize */

And probably someone is going to post to suggest just that. The above code will work most of the time, but it has two problems. First, mktime() is allowed to fail, so it's as well to check for it and report the error.

Second, it finds the date 24 hours ago. If there was a daylight saving time transition, 24 hours ago might be today or the day before yesterday.

You might think to fix the first problem by adding the line:

	x.tm_isdst = -1;
at the top, effectively forcing mktime() to work by wall clock time. Now you are asking for the same time yesterday, rather than the time 24 hours ago. But this doesn't fix the problem -- in the case of a daylight saving time transition, ``the same time yesterday'' might be ambiguous or not exist. In such a case a failure of mktime() is allowed (or perhaps required, depending on how one interprets the standard).

The correct fix is to let the daylight saving time transition take its effect by leaving tm_isdst alone, but to ask for the time 24 hours before a time near noon today. Even if that time is 11 am or 1 pm, it will still be yesterday. So the final version of the code is:

	x.tm_day--;      	/* back 1 day (well, 24 hours) */
	x.tm_hour = 12;         /* from some time between noon and 1 pm */

				/* and normalize */
	if (mktime (&x) == (time_t) -1) {
		fprintf (stderr, "mktime failed!!\n");
		exit(1);
	}

The structure now contains the correct year, month, and day values for yesterday. As always, remember that the representation of a struct tm requires you to add 1900 to the year and 1 to the month to get the correct values if displaying them numerically.

There is a terser version of the ``final'' code: replace the first two lines by

	x.tm_hour = -12;	/* 12 hours before sometime between
				 * midnight and 1 am this morning */
but the meaning of this is less obvious, so I don't recommend it.

-- 
Mark Brader                  "To err is human, but to really mess things up
msb@sq.com                    you need a timetable planner!"
SoftQuad Inc., Toronto                               -- Richard Porter

My text in this article is in the public domain.