154359Sroberto/* 254359Sroberto * Copyright (c) 1987, 1989 Regents of the University of California. 354359Sroberto * All rights reserved. 454359Sroberto * 554359Sroberto * This code is derived from software contributed to Berkeley by 654359Sroberto * Arthur David Olson of the National Cancer Institute. 754359Sroberto * 854359Sroberto * Redistribution and use in source and binary forms, with or without 954359Sroberto * modification, are permitted provided that the following conditions 1054359Sroberto * are met: 1154359Sroberto * 1. Redistributions of source code must retain the above copyright 1254359Sroberto * notice, this list of conditions and the following disclaimer. 1354359Sroberto * 2. Redistributions in binary form must reproduce the above copyright 1454359Sroberto * notice, this list of conditions and the following disclaimer in the 1554359Sroberto * documentation and/or other materials provided with the distribution. 1654359Sroberto * 3. All advertising materials mentioning features or use of this software 1754359Sroberto * must display the following acknowledgement: 1854359Sroberto * This product includes software developed by the University of 1954359Sroberto * California, Berkeley and its contributors. 2054359Sroberto * 4. Neither the name of the University nor the names of its contributors 2154359Sroberto * may be used to endorse or promote products derived from this software 2254359Sroberto * without specific prior written permission. 2354359Sroberto * 2454359Sroberto * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2554359Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2654359Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2754359Sroberto * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2854359Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2954359Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3054359Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3154359Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3254359Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3354359Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3454359Sroberto * SUCH DAMAGE. */ 3554359Sroberto 3654359Sroberto/*static char *sccsid = "from: @(#)ctime.c 5.26 (Berkeley) 2/23/91";*/ 3754359Sroberto 3854359Sroberto/* 3954359Sroberto * This implementation of mktime is lifted straight from the NetBSD (BSD 4.4) 4054359Sroberto * version. I modified it slightly to divorce it from the internals of the 4154359Sroberto * ctime library. Thus this version can't use details of the internal 4254359Sroberto * timezone state file to figure out strange unnormalized struct tm values, 4354359Sroberto * as might result from someone doing date math on the tm struct then passing 4454359Sroberto * it to mktime. 4554359Sroberto * 4654359Sroberto * It just does as well as it can at normalizing the tm input, then does a 4754359Sroberto * binary search of the time space using the system's localtime() function. 4854359Sroberto * 4954359Sroberto * The original binary search was defective in that it didn't consider the 5054359Sroberto * setting of tm_isdst when comparing tm values, causing the search to be 5154359Sroberto * flubbed for times near the dst/standard time changeover. The original 5254359Sroberto * code seems to make up for this by grubbing through the timezone info 5354359Sroberto * whenever the binary search barfed. Since I don't have that luxury in 5454359Sroberto * portable code, I have to take care of tm_isdst in the comparison routine. 5554359Sroberto * This requires knowing how many minutes offset dst is from standard time. 5654359Sroberto * 5754359Sroberto * So, if you live somewhere in the world where dst is not 60 minutes offset, 5854359Sroberto * and your vendor doesn't supply mktime(), you'll have to edit this variable 5954359Sroberto * by hand. Sorry about that. 6054359Sroberto */ 6154359Sroberto 6282498Sroberto#include "ntp_machine.h" 6354359Sroberto 64182007Sroberto#if !defined(HAVE_MKTIME) || !defined(HAVE_TIMEGM) 65106163Sroberto 6654359Sroberto#ifndef DSTMINUTES 6754359Sroberto#define DSTMINUTES 60 6854359Sroberto#endif 6954359Sroberto 7054359Sroberto#define FALSE 0 7154359Sroberto#define TRUE 1 7254359Sroberto 7354359Sroberto/* some constants from tzfile.h */ 7454359Sroberto#define SECSPERMIN 60 7554359Sroberto#define MINSPERHOUR 60 7654359Sroberto#define HOURSPERDAY 24 7754359Sroberto#define DAYSPERWEEK 7 7854359Sroberto#define DAYSPERNYEAR 365 7954359Sroberto#define DAYSPERLYEAR 366 8054359Sroberto#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) 8154359Sroberto#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) 8254359Sroberto#define MONSPERYEAR 12 8354359Sroberto#define TM_YEAR_BASE 1900 8454359Sroberto#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) 8554359Sroberto 8654359Srobertostatic int mon_lengths[2][MONSPERYEAR] = { 8754359Sroberto { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 8854359Sroberto { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 8954359Sroberto}; 9054359Sroberto 9154359Srobertostatic int year_lengths[2] = { 9254359Sroberto DAYSPERNYEAR, DAYSPERLYEAR 9354359Sroberto}; 9454359Sroberto 9554359Sroberto/* 9654359Sroberto** Adapted from code provided by Robert Elz, who writes: 9754359Sroberto** The "best" way to do mktime I think is based on an idea of Bob 9854359Sroberto** Kridle's (so its said...) from a long time ago. (mtxinu!kridle now). 9954359Sroberto** It does a binary search of the time_t space. Since time_t's are 10054359Sroberto** just 32 bits, its a max of 32 iterations (even at 64 bits it 10154359Sroberto** would still be very reasonable). 10254359Sroberto*/ 10354359Sroberto 10454359Sroberto#ifndef WRONG 10554359Sroberto#define WRONG (-1) 10654359Sroberto#endif /* !defined WRONG */ 10754359Sroberto 10854359Srobertostatic void 10954359Srobertonormalize( 11054359Sroberto int * tensptr, 11154359Sroberto int * unitsptr, 11254359Sroberto int base 11354359Sroberto ) 11454359Sroberto{ 11554359Sroberto if (*unitsptr >= base) { 11654359Sroberto *tensptr += *unitsptr / base; 11754359Sroberto *unitsptr %= base; 11854359Sroberto } else if (*unitsptr < 0) { 11954359Sroberto --*tensptr; 12054359Sroberto *unitsptr += base; 12154359Sroberto if (*unitsptr < 0) { 12254359Sroberto *tensptr -= 1 + (-*unitsptr) / base; 12354359Sroberto *unitsptr = base - (-*unitsptr) % base; 12454359Sroberto } 12554359Sroberto } 12654359Sroberto} 12754359Sroberto 12854359Srobertostatic struct tm * 12954359Srobertomkdst( 13054359Sroberto struct tm * tmp 13154359Sroberto ) 13254359Sroberto{ 13354359Sroberto /* jds */ 13454359Sroberto static struct tm tmbuf; 13554359Sroberto 13654359Sroberto tmbuf = *tmp; 13754359Sroberto tmbuf.tm_isdst = 1; 13854359Sroberto tmbuf.tm_min += DSTMINUTES; 13954359Sroberto normalize(&tmbuf.tm_hour, &tmbuf.tm_min, MINSPERHOUR); 14054359Sroberto return &tmbuf; 14154359Sroberto} 14254359Sroberto 14354359Srobertostatic int 14454359Srobertotmcomp( 14554359Sroberto register struct tm * atmp, 14654359Sroberto register struct tm * btmp 14754359Sroberto ) 14854359Sroberto{ 14954359Sroberto register int result; 15054359Sroberto 15154359Sroberto /* compare down to the same day */ 15254359Sroberto 15354359Sroberto if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && 15454359Sroberto (result = (atmp->tm_mon - btmp->tm_mon)) == 0) 15554359Sroberto result = (atmp->tm_mday - btmp->tm_mday); 15654359Sroberto 15754359Sroberto if(result != 0) 15854359Sroberto return result; 15954359Sroberto 16054359Sroberto /* get rid of one-sided dst bias */ 16154359Sroberto 16254359Sroberto if(atmp->tm_isdst == 1 && !btmp->tm_isdst) 16354359Sroberto btmp = mkdst(btmp); 16454359Sroberto else if(btmp->tm_isdst == 1 && !atmp->tm_isdst) 16554359Sroberto atmp = mkdst(atmp); 16654359Sroberto 16754359Sroberto /* compare the rest of the way */ 16854359Sroberto 16954359Sroberto if ((result = (atmp->tm_hour - btmp->tm_hour)) == 0 && 17054359Sroberto (result = (atmp->tm_min - btmp->tm_min)) == 0) 17154359Sroberto result = atmp->tm_sec - btmp->tm_sec; 17254359Sroberto return result; 17354359Sroberto} 17454359Sroberto 17554359Sroberto 17654359Srobertostatic time_t 17754359Srobertotime2( 17854359Sroberto struct tm * tmp, 179132451Sroberto int * okayp, 180132451Sroberto int usezn 18154359Sroberto ) 18254359Sroberto{ 18354359Sroberto register int dir; 18454359Sroberto register int bits; 18554359Sroberto register int i; 18654359Sroberto register int saved_seconds; 18754359Sroberto time_t t; 18854359Sroberto struct tm yourtm, mytm; 18954359Sroberto 19054359Sroberto *okayp = FALSE; 19154359Sroberto yourtm = *tmp; 19254359Sroberto if (yourtm.tm_sec >= SECSPERMIN + 2 || yourtm.tm_sec < 0) 19354359Sroberto normalize(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN); 19454359Sroberto normalize(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR); 19554359Sroberto normalize(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY); 19654359Sroberto normalize(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR); 19754359Sroberto while (yourtm.tm_mday <= 0) { 19854359Sroberto --yourtm.tm_year; 19954359Sroberto yourtm.tm_mday += 20054359Sroberto year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)]; 20154359Sroberto } 20254359Sroberto for ( ; ; ) { 20354359Sroberto i = mon_lengths[isleap(yourtm.tm_year + 20454359Sroberto TM_YEAR_BASE)][yourtm.tm_mon]; 20554359Sroberto if (yourtm.tm_mday <= i) 20654359Sroberto break; 20754359Sroberto yourtm.tm_mday -= i; 20854359Sroberto if (++yourtm.tm_mon >= MONSPERYEAR) { 20954359Sroberto yourtm.tm_mon = 0; 21054359Sroberto ++yourtm.tm_year; 21154359Sroberto } 21254359Sroberto } 21354359Sroberto saved_seconds = yourtm.tm_sec; 21454359Sroberto yourtm.tm_sec = 0; 21554359Sroberto /* 21654359Sroberto ** Calculate the number of magnitude bits in a time_t 21754359Sroberto ** (this works regardless of whether time_t is 21854359Sroberto ** signed or unsigned, though lint complains if unsigned). 21954359Sroberto */ 22054359Sroberto for (bits = 0, t = 1; t > 0; ++bits, t <<= 1) 22154359Sroberto ; 22254359Sroberto /* 22354359Sroberto ** If time_t is signed, then 0 is the median value, 22454359Sroberto ** if time_t is unsigned, then 1 << bits is median. 22554359Sroberto */ 22654359Sroberto t = (t < 0) ? 0 : ((time_t) 1 << bits); 22754359Sroberto for ( ; ; ) { 228132451Sroberto if (usezn) 229132451Sroberto mytm = *localtime(&t); 230132451Sroberto else 231132451Sroberto mytm = *gmtime(&t); 23254359Sroberto dir = tmcomp(&mytm, &yourtm); 23354359Sroberto if (dir != 0) { 23454359Sroberto if (bits-- < 0) 23554359Sroberto return WRONG; 23654359Sroberto if (bits < 0) 23754359Sroberto --t; 23854359Sroberto else if (dir > 0) 23954359Sroberto t -= (time_t) 1 << bits; 24054359Sroberto else t += (time_t) 1 << bits; 24154359Sroberto continue; 24254359Sroberto } 24354359Sroberto if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) 24454359Sroberto break; 24554359Sroberto 24654359Sroberto return WRONG; 24754359Sroberto } 24854359Sroberto t += saved_seconds; 249132451Sroberto if (usezn) 250132451Sroberto *tmp = *localtime(&t); 251132451Sroberto else 252132451Sroberto *tmp = *gmtime(&t); 25354359Sroberto *okayp = TRUE; 25454359Sroberto return t; 25554359Sroberto} 256132451Sroberto#else 257132451Srobertoint mktime_bs; 258132451Sroberto#endif /* !HAVE_MKTIME || !HAVE_TIMEGM */ 25954359Sroberto 260182007Sroberto#ifndef HAVE_MKTIME 26154359Srobertostatic time_t 26254359Srobertotime1( 26354359Sroberto struct tm * tmp 26454359Sroberto ) 26554359Sroberto{ 26654359Sroberto register time_t t; 26754359Sroberto int okay; 26854359Sroberto 26954359Sroberto if (tmp->tm_isdst > 1) 27054359Sroberto tmp->tm_isdst = 1; 271132451Sroberto t = time2(tmp, &okay, 1); 27254359Sroberto if (okay || tmp->tm_isdst < 0) 27354359Sroberto return t; 27454359Sroberto 27554359Sroberto return WRONG; 27654359Sroberto} 27754359Sroberto 27854359Srobertotime_t 27954359Srobertomktime( 28054359Sroberto struct tm * tmp 28154359Sroberto ) 28254359Sroberto{ 28354359Sroberto return time1(tmp); 28454359Sroberto} 285132451Sroberto#endif /* !HAVE_MKTIME */ 286132451Sroberto 287182007Sroberto#ifndef HAVE_TIMEGM 288132451Srobertotime_t 289132451Srobertotimegm( 290132451Sroberto struct tm * tmp 291132451Sroberto ) 292132451Sroberto{ 293132451Sroberto register time_t t; 294132451Sroberto int okay; 295132451Sroberto 296132451Sroberto tmp->tm_isdst = 0; 297132451Sroberto t = time2(tmp, &okay, 0); 298132451Sroberto if (okay || tmp->tm_isdst < 0) 299132451Sroberto return t; 300132451Sroberto 301132451Sroberto return WRONG; 302132451Sroberto} 303132451Sroberto#endif /* !HAVE_TIMEGM */ 304