1 /* $NetBSD: mktime.c,v 1.1.1.2 2012/01/31 21:24:08 kardel Exp $ */ 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 92 static 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 97 static 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 114 static void 115 normalize( 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 134 static struct tm * 135 mkdst( 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 149 static int 150 tmcomp( 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 182 static time_t 183 time2( 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 263 int mktime_bs; 264 #endif /* !HAVE_MKTIME || !HAVE_TIMEGM */ 265 266 #ifndef HAVE_MKTIME 267 static time_t 268 time1( 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 284 time_t 285 mktime( 286 struct tm * tmp 287 ) 288 { 289 return time1(tmp); 290 } 291 #endif /* !HAVE_MKTIME */ 292 293 #ifndef HAVE_TIMEGM 294 time_t 295 timegm( 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