Inverse for localtime().

Paul Reger paulr at sequent.UUCP
Sat Aug 26 11:06:54 AEST 1989


A while back someone asked if there was such a thing as an inverse
localtime() function. (localtime() maps a time_t * to a struct *tm).
I guess there isn't one, so I wrote one (The problem interested me
since it seemed natural for one to exist.).  Here it is:
 
#define Seconds_per_minute 60
#define Seconds_per_hour   60*Seconds_per_minute
#define Seconds_per_day    24*Seconds_per_hour
#define Seconds_per_month  30*Seconds_per_day
#define Seconds_per_year   365*Seconds_per_day

#include <sys/types.h>
#include <sys/time.h>

time_t
tm_to_time_t(t)
struct tm t;
{
    time_t high_guess = 0,low_guess = 0,middle;
    struct tm *temp;
    int score_high,score_low,nyears;
    static const int fudges[12] = { 0*Seconds_per_day, /* Jan. */
				    1*Seconds_per_day, /* Feb. */
				   -1*Seconds_per_day, /* Mar. */
				    0*Seconds_per_day, /* Apr. */
				    0*Seconds_per_day, /* May  */
				    1*Seconds_per_day, /* Jun. */
				    1*Seconds_per_day, /* Jul. */
				    2*Seconds_per_day, /* Aug. */
				    3*Seconds_per_day, /* Sep. */
				    3*Seconds_per_day, /* Oct. */
				    4*Seconds_per_day, /* Nov. */
				    4*Seconds_per_day  /* Dec. */    };

    nyears = t.tm_year - 70;
    low_guess = Seconds_per_year*nyears +
             (double) ((nyears-1)*Seconds_per_day) * 0.24 +  /* add in leap days... */
	     Seconds_per_month*t.tm_mon +
	     fudges[t.tm_mon] +                         /* Add in approximate days for 'non-30' day months. */
	    Seconds_per_day*(t.tm_mday - 1) + Seconds_per_hour*t.tm_hour + Seconds_per_minute*t.tm_min + t.tm_sec;
    high_guess = low_guess + 2*Seconds_per_day;  /* The initial guess will
    	    	    	    	        	    always be about 1 and a maximum of two days off.  Proof
    	    	    	    	        	    left to the reader. */

    while (abs((high_guess - low_guess)) > 1) {
	middle = (high_guess + low_guess) / 2;
	temp = localtime(&middle);

	score_high = ((temp->tm_year > t.tm_year) << 5) + ((temp->tm_mon > t.tm_mon) << 4) + ((temp->tm_mday > t.tm_mday) << 3) +
		((temp->tm_hour > t.tm_hour) << 2) + ((temp->tm_min > t.tm_min) << 1) + (temp->tm_sec > t.tm_sec);
	score_low = ((temp->tm_year < t.tm_year) << 5) + ((temp->tm_mon < t.tm_mon) << 4) + ((temp->tm_mday < t.tm_mday) << 3) +
		((temp->tm_hour < t.tm_hour) << 2) + ((temp->tm_min < t.tm_min) << 1) + (temp->tm_sec < t.tm_sec);

	if (score_high > score_low)
		high_guess = middle;
	else
		low_guess = middle;
    }
    return middle;
}

main(argc,argv)
    int argc;
    char *argv[];
{
    struct tm z;
    time_t    t;

    z.tm_hour = atoi(argv[1]);
    z.tm_min  = atoi(argv[2]);
    z.tm_sec  = atoi(argv[3]);
    z.tm_mday = atoi(argv[4]);
    z.tm_mon  = atoi(argv[5]);
    z.tm_year = atoi(argv[6]);

    t = tm_to_time_t(z);
    printf("%d is the time representation for: %s",t,ctime(&t));
}


All the standard disclaimers apply.  I only tested this a little bit.

Also, my solution is based on a binary search and is very costly.
Anyone know of a better solution ??

Note also that the elements in the struct besides the six above tm are
ignored.

Still also note that for some reason, the seconds are off by one some
times.



More information about the Comp.unix.questions mailing list