1/* $NetBSD$ */ 2 3/* 4 * Copyright (c) 1987, 1989 Regents of the University of California. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Arthur David Olson of the National Cancer Institute. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. */ 37 38/*static char *sccsid = "from: @(#)ctime.c 5.26 (Berkeley) 2/23/91";*/ 39 40/* 41 * This implementation of mktime is lifted straight from the NetBSD (BSD 4.4) 42 * version. I modified it slightly to divorce it from the internals of the 43 * ctime library. Thus this version can't use details of the internal 44 * timezone state file to figure out strange unnormalized struct tm values, 45 * as might result from someone doing date math on the tm struct then passing 46 * it to mktime. 47 * 48 * It just does as well as it can at normalizing the tm input, then does a 49 * binary search of the time space using the system's localtime() function. 50 * 51 * The original binary search was defective in that it didn't consider the 52 * setting of tm_isdst when comparing tm values, causing the search to be 53 * flubbed for times near the dst/standard time changeover. The original 54 * code seems to make up for this by grubbing through the timezone info 55 * whenever the binary search barfed. Since I don't have that luxury in 56 * portable code, I have to take care of tm_isdst in the comparison routine. 57 * This requires knowing how many minutes offset dst is from standard time. 58 * 59 * So, if you live somewhere in the world where dst is not 60 minutes offset, 60 * and your vendor doesn't supply mktime(), you'll have to edit this variable 61 * by hand. Sorry about that. 62 */ 63 64#include "ntp_machine.h" 65 66#if !defined(HAVE_MKTIME) || !defined(HAVE_TIMEGM) 67 68#if SIZEOF_TIME_T >= 8 69#error libntp supplied mktime()/timegm() do not support 64-bit time_t 70#endif 71 72#ifndef DSTMINUTES 73#define DSTMINUTES 60 74#endif 75 76#define FALSE 0 77#define TRUE 1 78 79/* some constants from tzfile.h */ 80#define SECSPERMIN 60 81#define MINSPERHOUR 60 82#define HOURSPERDAY 24 83#define DAYSPERWEEK 7 84#define DAYSPERNYEAR 365 85#define DAYSPERLYEAR 366 86#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) 87#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) 88#define MONSPERYEAR 12 89#define TM_YEAR_BASE 1900 90#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) 91 92static int mon_lengths[2][MONSPERYEAR] = { 93 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 94 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 95}; 96 97static int year_lengths[2] = { 98 DAYSPERNYEAR, DAYSPERLYEAR 99}; 100 101/* 102** Adapted from code provided by Robert Elz, who writes: 103** The "best" way to do mktime I think is based on an idea of Bob 104** Kridle's (so its said...) from a long time ago. (mtxinu!kridle now). 105** It does a binary search of the time_t space. Since time_t's are 106** just 32 bits, its a max of 32 iterations (even at 64 bits it 107** would still be very reasonable). 108*/ 109 110#ifndef WRONG 111#define WRONG (-1) 112#endif /* !defined WRONG */ 113 114static void 115normalize( 116 int * tensptr, 117 int * unitsptr, 118 int base 119 ) 120{ 121 if (*unitsptr >= base) { 122 *tensptr += *unitsptr / base; 123 *unitsptr %= base; 124 } else if (*unitsptr < 0) { 125 --*tensptr; 126 *unitsptr += base; 127 if (*unitsptr < 0) { 128 *tensptr -= 1 + (-*unitsptr) / base; 129 *unitsptr = base - (-*unitsptr) % base; 130 } 131 } 132} 133 134static struct tm * 135mkdst( 136 struct tm * tmp 137 ) 138{ 139 /* jds */ 140 static struct tm tmbuf; 141 142 tmbuf = *tmp; 143 tmbuf.tm_isdst = 1; 144 tmbuf.tm_min += DSTMINUTES; 145 normalize(&tmbuf.tm_hour, &tmbuf.tm_min, MINSPERHOUR); 146 return &tmbuf; 147} 148 149static int 150tmcomp( 151 register struct tm * atmp, 152 register struct tm * btmp 153 ) 154{ 155 register int result; 156 157 /* compare down to the same day */ 158 159 if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && 160 (result = (atmp->tm_mon - btmp->tm_mon)) == 0) 161 result = (atmp->tm_mday - btmp->tm_mday); 162 163 if(result != 0) 164 return result; 165 166 /* get rid of one-sided dst bias */ 167 168 if(atmp->tm_isdst == 1 && !btmp->tm_isdst) 169 btmp = mkdst(btmp); 170 else if(btmp->tm_isdst == 1 && !atmp->tm_isdst) 171 atmp = mkdst(atmp); 172 173 /* compare the rest of the way */ 174 175 if ((result = (atmp->tm_hour - btmp->tm_hour)) == 0 && 176 (result = (atmp->tm_min - btmp->tm_min)) == 0) 177 result = atmp->tm_sec - btmp->tm_sec; 178 return result; 179} 180 181 182static time_t 183time2( 184 struct tm * tmp, 185 int * okayp, 186 int usezn 187 ) 188{ 189 register int dir; 190 register int bits; 191 register int i; 192 register int saved_seconds; 193 time_t t; 194 struct tm yourtm, mytm; 195 196 *okayp = FALSE; 197 yourtm = *tmp; 198 if (yourtm.tm_sec >= SECSPERMIN + 2 || yourtm.tm_sec < 0) 199 normalize(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN); 200 normalize(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR); 201 normalize(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY); 202 normalize(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR); 203 while (yourtm.tm_mday <= 0) { 204 --yourtm.tm_year; 205 yourtm.tm_mday += 206 year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)]; 207 } 208 for ( ; ; ) { 209 i = mon_lengths[isleap(yourtm.tm_year + 210 TM_YEAR_BASE)][yourtm.tm_mon]; 211 if (yourtm.tm_mday <= i) 212 break; 213 yourtm.tm_mday -= i; 214 if (++yourtm.tm_mon >= MONSPERYEAR) { 215 yourtm.tm_mon = 0; 216 ++yourtm.tm_year; 217 } 218 } 219 saved_seconds = yourtm.tm_sec; 220 yourtm.tm_sec = 0; 221 /* 222 ** Calculate the number of magnitude bits in a time_t 223 ** (this works regardless of whether time_t is 224 ** signed or unsigned, though lint complains if unsigned). 225 */ 226 for (bits = 0, t = 1; t > 0; ++bits, t <<= 1) 227 ; 228 /* 229 ** If time_t is signed, then 0 is the median value, 230 ** if time_t is unsigned, then 1 << bits is median. 231 */ 232 t = (t < 0) ? 0 : ((time_t) 1 << bits); 233 for ( ; ; ) { 234 if (usezn) 235 mytm = *localtime(&t); 236 else 237 mytm = *gmtime(&t); 238 dir = tmcomp(&mytm, &yourtm); 239 if (dir != 0) { 240 if (bits-- < 0) 241 return WRONG; 242 if (bits < 0) 243 --t; 244 else if (dir > 0) 245 t -= (time_t) 1 << bits; 246 else t += (time_t) 1 << bits; 247 continue; 248 } 249 if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) 250 break; 251 252 return WRONG; 253 } 254 t += saved_seconds; 255 if (usezn) 256 *tmp = *localtime(&t); 257 else 258 *tmp = *gmtime(&t); 259 *okayp = TRUE; 260 return t; 261} 262#else 263int mktime_bs; 264#endif /* !HAVE_MKTIME || !HAVE_TIMEGM */ 265 266#ifndef HAVE_MKTIME 267static time_t 268time1( 269 struct tm * tmp 270 ) 271{ 272 register time_t t; 273 int okay; 274 275 if (tmp->tm_isdst > 1) 276 tmp->tm_isdst = 1; 277 t = time2(tmp, &okay, 1); 278 if (okay || tmp->tm_isdst < 0) 279 return t; 280 281 return WRONG; 282} 283 284time_t 285mktime( 286 struct tm * tmp 287 ) 288{ 289 return time1(tmp); 290} 291#endif /* !HAVE_MKTIME */ 292 293#ifndef HAVE_TIMEGM 294time_t 295timegm( 296 struct tm * tmp 297 ) 298{ 299 register time_t t; 300 int okay; 301 302 tmp->tm_isdst = 0; 303 t = time2(tmp, &okay, 0); 304 if (okay || tmp->tm_isdst < 0) 305 return t; 306 307 return WRONG; 308} 309#endif /* !HAVE_TIMEGM */ 310