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