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 93static 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 98static 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 115static void 116normalize( 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 135static struct tm * 136mkdst( 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 150static int 151tmcomp( 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 183static time_t 184time2( 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 264int mktime_bs; 265#endif /* !HAVE_MKTIME || !HAVE_TIMEGM */ 266 267#ifndef HAVE_MKTIME 268static time_t 269time1( 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 285time_t 286mktime( 287 struct tm * tmp 288 ) 289{ 290 return time1(tmp); 291} 292#endif /* !HAVE_MKTIME */ 293 294#ifdef WANT_TIMEGM 295#ifndef HAVE_TIMEGM 296time_t 297timegm( 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