1 /* $NetBSD: mktime.c,v 1.5 2020/05/25 20:47:24 christos 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 <config.h> 65 #include "ntp_machine.h" 66 67 #if !defined(HAVE_MKTIME) || ( !defined(HAVE_TIMEGM) && defined(WANT_TIMEGM) ) 68 69 #if SIZEOF_TIME_T >= 8 70 #error libntp supplied mktime()/timegm() do not support 64-bit time_t 71 #endif 72 73 #ifndef DSTMINUTES 74 #define DSTMINUTES 60 75 #endif 76 77 #define FALSE 0 78 #define TRUE 1 79 80 /* some constants from tzfile.h */ 81 #define SECSPERMIN 60 82 #define MINSPERHOUR 60 83 #define HOURSPERDAY 24 84 #define DAYSPERWEEK 7 85 #define DAYSPERNYEAR 365 86 #define DAYSPERLYEAR 366 87 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) 88 #define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) 89 #define MONSPERYEAR 12 90 #define TM_YEAR_BASE 1900 91 #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) 92 93 static int mon_lengths[2][MONSPERYEAR] = { 94 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 95 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 96 }; 97 98 static int year_lengths[2] = { 99 DAYSPERNYEAR, DAYSPERLYEAR 100 }; 101 102 /* 103 ** Adapted from code provided by Robert Elz, who writes: 104 ** The "best" way to do mktime I think is based on an idea of Bob 105 ** Kridle's (so its said...) from a long time ago. (mtxinu!kridle now). 106 ** It does a binary search of the time_t space. Since time_t's are 107 ** just 32 bits, its a max of 32 iterations (even at 64 bits it 108 ** would still be very reasonable). 109 */ 110 111 #ifndef WRONG 112 #define WRONG (-1) 113 #endif /* !defined WRONG */ 114 115 static void 116 normalize( 117 int * tensptr, 118 int * unitsptr, 119 int base 120 ) 121 { 122 if (*unitsptr >= base) { 123 *tensptr += *unitsptr / base; 124 *unitsptr %= base; 125 } else if (*unitsptr < 0) { 126 --*tensptr; 127 *unitsptr += base; 128 if (*unitsptr < 0) { 129 *tensptr -= 1 + (-*unitsptr) / base; 130 *unitsptr = base - (-*unitsptr) % base; 131 } 132 } 133 } 134 135 static struct tm * 136 mkdst( 137 struct tm * tmp 138 ) 139 { 140 /* jds */ 141 static struct tm tmbuf; 142 143 tmbuf = *tmp; 144 tmbuf.tm_isdst = 1; 145 tmbuf.tm_min += DSTMINUTES; 146 normalize(&tmbuf.tm_hour, &tmbuf.tm_min, MINSPERHOUR); 147 return &tmbuf; 148 } 149 150 static int 151 tmcomp( 152 register struct tm * atmp, 153 register struct tm * btmp 154 ) 155 { 156 register int result; 157 158 /* compare down to the same day */ 159 160 if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && 161 (result = (atmp->tm_mon - btmp->tm_mon)) == 0) 162 result = (atmp->tm_mday - btmp->tm_mday); 163 164 if(result != 0) 165 return result; 166 167 /* get rid of one-sided dst bias */ 168 169 if(atmp->tm_isdst == 1 && !btmp->tm_isdst) 170 btmp = mkdst(btmp); 171 else if(btmp->tm_isdst == 1 && !atmp->tm_isdst) 172 atmp = mkdst(atmp); 173 174 /* compare the rest of the way */ 175 176 if ((result = (atmp->tm_hour - btmp->tm_hour)) == 0 && 177 (result = (atmp->tm_min - btmp->tm_min)) == 0) 178 result = atmp->tm_sec - btmp->tm_sec; 179 return result; 180 } 181 182 183 static time_t 184 time2( 185 struct tm * tmp, 186 int * okayp, 187 int usezn 188 ) 189 { 190 register int dir; 191 register int bits; 192 register int i; 193 register int saved_seconds; 194 time_t t; 195 struct tm yourtm, mytm; 196 197 *okayp = FALSE; 198 yourtm = *tmp; 199 if (yourtm.tm_sec >= SECSPERMIN + 2 || yourtm.tm_sec < 0) 200 normalize(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN); 201 normalize(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR); 202 normalize(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY); 203 normalize(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR); 204 while (yourtm.tm_mday <= 0) { 205 --yourtm.tm_year; 206 yourtm.tm_mday += 207 year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)]; 208 } 209 for ( ; ; ) { 210 i = mon_lengths[isleap(yourtm.tm_year + 211 TM_YEAR_BASE)][yourtm.tm_mon]; 212 if (yourtm.tm_mday <= i) 213 break; 214 yourtm.tm_mday -= i; 215 if (++yourtm.tm_mon >= MONSPERYEAR) { 216 yourtm.tm_mon = 0; 217 ++yourtm.tm_year; 218 } 219 } 220 saved_seconds = yourtm.tm_sec; 221 yourtm.tm_sec = 0; 222 /* 223 ** Calculate the number of magnitude bits in a time_t 224 ** (this works regardless of whether time_t is 225 ** signed or unsigned, though lint complains if unsigned). 226 */ 227 for (bits = 0, t = 1; t > 0; ++bits, t <<= 1) 228 ; 229 /* 230 ** If time_t is signed, then 0 is the median value, 231 ** if time_t is unsigned, then 1 << bits is median. 232 */ 233 t = (t < 0) ? 0 : ((time_t) 1 << bits); 234 for ( ; ; ) { 235 if (usezn) 236 mytm = *localtime(&t); 237 else 238 mytm = *gmtime(&t); 239 dir = tmcomp(&mytm, &yourtm); 240 if (dir != 0) { 241 if (bits-- < 0) 242 return WRONG; 243 if (bits < 0) 244 --t; 245 else if (dir > 0) 246 t -= (time_t) 1 << bits; 247 else t += (time_t) 1 << bits; 248 continue; 249 } 250 if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) 251 break; 252 253 return WRONG; 254 } 255 t += saved_seconds; 256 if (usezn) 257 *tmp = *localtime(&t); 258 else 259 *tmp = *gmtime(&t); 260 *okayp = TRUE; 261 return t; 262 } 263 #else 264 int mktime_bs; 265 #endif /* !HAVE_MKTIME || !HAVE_TIMEGM */ 266 267 #ifndef HAVE_MKTIME 268 static time_t 269 time1( 270 struct tm * tmp 271 ) 272 { 273 register time_t t; 274 int okay; 275 276 if (tmp->tm_isdst > 1) 277 tmp->tm_isdst = 1; 278 t = time2(tmp, &okay, 1); 279 if (okay || tmp->tm_isdst < 0) 280 return t; 281 282 return WRONG; 283 } 284 285 time_t 286 mktime( 287 struct tm * tmp 288 ) 289 { 290 return time1(tmp); 291 } 292 #endif /* !HAVE_MKTIME */ 293 294 #ifdef WANT_TIMEGM 295 #ifndef HAVE_TIMEGM 296 time_t 297 timegm( 298 struct tm * tmp 299 ) 300 { 301 register time_t t; 302 int okay; 303 304 tmp->tm_isdst = 0; 305 t = time2(tmp, &okay, 0); 306 if (okay || tmp->tm_isdst < 0) 307 return t; 308 309 return WRONG; 310 } 311 #endif /* !HAVE_TIMEGM */ 312 #endif /* WANT_TIMEGM */ 313