Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!cs.utexas.edu!uunet!zephyr.ens.tek.com!tektronix!sequent!paulr From: paulr@sequent.UUCP (Paul Reger) Newsgroups: comp.unix.questions Subject: Inverse for localtime(). Keywords: localtime(), ctime, struct tm Message-ID: <20750@sequent.UUCP> Date: 26 Aug 89 01:06:54 GMT Reply-To: paulr@sequent.UUCP (Paul Reger) Organization: Sequent Computer Systems, Inc Lines: 90 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 #include 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.