Skip to content
Snippets Groups Projects
localtime.c 37.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    			if (!strcmp(sp->name,zone))
    				break;
    			sp = sp->next;
    		}
    
    	if (sp == NULL) {
    		ast_tzsetwall();
    		sp = lclptr;
    		/* Find the default zone record */
    		while (sp != NULL) {
    			if (sp->name[0] == '\0')
    				break;
    			sp = sp->next;
    		}
    	}
    
    	/* Last ditch effort, use GMT */
    
    	if (sp == NULL) {
    		gmtsub(timep, offset, tmp, zone);
    		return;
    	}
    	if (sp->timecnt == 0 || t < sp->ats[0]) {
    		i = 0;
    		while (sp->ttis[i].tt_isdst)
    			if (++i >= sp->typecnt) {
    				i = 0;
    				break;
    			}
    	} else {
    		for (i = 1; i < sp->timecnt; ++i)
    			if (t < sp->ats[i])
    				break;
    		i = sp->types[i - 1];
    	}
    	ttisp = &sp->ttis[i];
    	/*
    	** To get (wrong) behavior that's compatible with System V Release 2.0
    	** you'd replace the statement below with
    	**	t += ttisp->tt_gmtoff;
    	**	timesub(&t, 0L, sp, tmp);
    	*/
    	timesub(&t, ttisp->tt_gmtoff, sp, tmp);
    	tmp->tm_isdst = ttisp->tt_isdst;
    	tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
    #ifdef TM_ZONE
    	tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
    #endif /* defined TM_ZONE */
    }
    
    struct tm *
    ast_localtime(timep, p_tm, zone)
    const time_t * const	timep;
    struct tm *p_tm;
    const char * const	zone;
    {
    #ifdef _THREAD_SAFE
    	ast_mutex_lock(&lcl_mutex);
    #endif
    	ast_tzset(zone);
    	localsub(timep, 0L, p_tm, zone);
    #ifdef _THREAD_SAFE
    	ast_mutex_unlock(&lcl_mutex);
    #endif
    	return(p_tm);
    }
    
    /*
    ** gmtsub is to gmtime as localsub is to localtime.
    */
    
    static void
    gmtsub(timep, offset, tmp, zone)
    const time_t * const	timep;
    const long		offset;
    struct tm * const	tmp;
    const char * const	zone;
    {
    #ifdef	_THREAD_SAFE
    	ast_mutex_lock(&gmt_mutex);
    #endif
    	if (!gmt_is_set) {
    		gmt_is_set = TRUE;
    		gmtptr = (struct state *) malloc(sizeof *gmtptr);
    		if (gmtptr != NULL)
    			gmtload(gmtptr);
    	}
    	ast_mutex_unlock(&gmt_mutex);
    	timesub(timep, offset, gmtptr, tmp);
    #ifdef TM_ZONE
    	/*
    	** Could get fancy here and deliver something such as
    	** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero,
    	** but this is no time for a treasure hunt.
    	*/
    	if (offset != 0)
    		tmp->TM_ZONE = wildabbr;
    	else {
    		if (gmtptr == NULL)
    			tmp->TM_ZONE = gmt;
    		else	tmp->TM_ZONE = gmtptr->chars;
    	}
    #endif /* defined TM_ZONE */
    }
    
    static void
    timesub(timep, offset, sp, tmp)
    const time_t * const			timep;
    const long				offset;
    register const struct state * const	sp;
    register struct tm * const		tmp;
    {
    	register const struct lsinfo *	lp;
    	register long			days;
    	register long			rem;
    	register int			y;
    	register int			yleap;
    	register const int *		ip;
    	register long			corr;
    	register int			hit;
    	register int			i;
    
    	corr = 0;
    	hit = 0;
    	i = (sp == NULL) ? 0 : sp->leapcnt;
    	while (--i >= 0) {
    		lp = &sp->lsis[i];
    		if (*timep >= lp->ls_trans) {
    			if (*timep == lp->ls_trans) {
    				hit = ((i == 0 && lp->ls_corr > 0) ||
    					lp->ls_corr > sp->lsis[i - 1].ls_corr);
    				if (hit)
    					while (i > 0 &&
    						sp->lsis[i].ls_trans ==
    						sp->lsis[i - 1].ls_trans + 1 &&
    						sp->lsis[i].ls_corr ==
    						sp->lsis[i - 1].ls_corr + 1) {
    							++hit;
    							--i;
    					}
    			}
    			corr = lp->ls_corr;
    			break;
    		}
    	}
    	days = *timep / SECSPERDAY;
    	rem = *timep % SECSPERDAY;
    #ifdef mc68k
    	if (*timep == 0x80000000) {
    		/*
    		** A 3B1 muffs the division on the most negative number.
    		*/
    		days = -24855;
    		rem = -11648;
    	}
    #endif /* defined mc68k */
    	rem += (offset - corr);
    	while (rem < 0) {
    		rem += SECSPERDAY;
    		--days;
    	}
    	while (rem >= SECSPERDAY) {
    		rem -= SECSPERDAY;
    		++days;
    	}
    	tmp->tm_hour = (int) (rem / SECSPERHOUR);
    	rem = rem % SECSPERHOUR;
    	tmp->tm_min = (int) (rem / SECSPERMIN);
    	/*
    	** A positive leap second requires a special
    	** representation.  This uses "... ??:59:60" et seq.
    	*/
    	tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
    	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
    	if (tmp->tm_wday < 0)
    		tmp->tm_wday += DAYSPERWEEK;
    	y = EPOCH_YEAR;
    #define LEAPS_THRU_END_OF(y)	((y) / 4 - (y) / 100 + (y) / 400)
    	while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
    		register int	newy;
    
    		newy = y + days / DAYSPERNYEAR;
    		if (days < 0)
    			--newy;
    		days -= (newy - y) * DAYSPERNYEAR +
    			LEAPS_THRU_END_OF(newy - 1) -
    			LEAPS_THRU_END_OF(y - 1);
    		y = newy;
    	}
    	tmp->tm_year = y - TM_YEAR_BASE;
    	tmp->tm_yday = (int) days;
    	ip = mon_lengths[yleap];
    	for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
    		days = days - (long) ip[tmp->tm_mon];
    	tmp->tm_mday = (int) (days + 1);
    	tmp->tm_isdst = 0;
    #ifdef TM_GMTOFF
    	tmp->TM_GMTOFF = offset;
    #endif /* defined TM_GMTOFF */
    }
    
    
    char *
    ast_ctime(timep)
    
    const time_t * const	timep;
    {
    /*
    ** Section 4.12.3.2 of X3.159-1989 requires that
    **	The ctime funciton converts the calendar time pointed to by timer
    **	to local time in the form of a string.  It is equivalent to
    **		asctime(localtime(timer))
    */
    	return asctime(localtime(timep));
    }
    
    
    char *
    ast_ctime_r(timep, buf)
    
    const time_t * const	timep;
    char *buf;
    {
            struct tm tm;
    
    #ifdef SOLARIS
    	return asctime_r(localtime_r(timep, &tm), buf, 256);
    #else
    
    	return asctime_r(localtime_r(timep, &tm), buf);
    
    }
    
    /*
    ** Adapted from code provided by Robert Elz, who writes:
    **	The "best" way to do mktime I think is based on an idea of Bob
    **	Kridle's (so its said...) from a long time ago.
    **	[kridle@xinet.com as of 1996-01-16.]
    **	It does a binary search of the time_t space.  Since time_t's are
    **	just 32 bits, its a max of 32 iterations (even at 64 bits it
    **	would still be very reasonable).
    */
    
    #ifndef WRONG
    #define WRONG	(-1)
    #endif /* !defined WRONG */
    
    /*
    ** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com).
    */
    
    static int
    increment_overflow(number, delta)
    int *	number;
    int	delta;
    {
    	int	number0;
    
    	number0 = *number;
    	*number += delta;
    	return (*number < number0) != (delta < 0);
    }
    
    static int
    normalize_overflow(tensptr, unitsptr, base)
    int * const	tensptr;
    int * const	unitsptr;
    const int	base;
    {
    	register int	tensdelta;
    
    	tensdelta = (*unitsptr >= 0) ?
    		(*unitsptr / base) :
    		(-1 - (-1 - *unitsptr) / base);
    	*unitsptr -= tensdelta * base;
    	return increment_overflow(tensptr, tensdelta);
    }
    
    static int
    tmcomp(atmp, btmp)
    register const struct tm * const atmp;
    register const struct tm * const btmp;
    {
    	register int	result;
    
    	if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
    		(result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
    		(result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
    		(result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
    		(result = (atmp->tm_min - btmp->tm_min)) == 0)
    			result = atmp->tm_sec - btmp->tm_sec;
    	return result;
    }
    
    static time_t
    time2(tmp, funcp, offset, okayp, zone)
    struct tm * const	tmp;
    void (* const		funcp) P((const time_t*, long, struct tm*, const char*));
    const long		offset;
    int * const		okayp;
    const char * const	zone;
    {
    	register const struct state *	sp;
    	register int			dir;
    	register int			bits;
    	register int			i, j ;
    	register int			saved_seconds;
    	time_t				newt;
    	time_t				t;
    	struct tm			yourtm, mytm;
    
    	*okayp = FALSE;
    	yourtm = *tmp;
    	if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
    		return WRONG;
    	if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
    		return WRONG;
    	if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
    		return WRONG;
    	/*
    	** Turn yourtm.tm_year into an actual year number for now.
    	** It is converted back to an offset from TM_YEAR_BASE later.
    	*/
    	if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
    		return WRONG;
    	while (yourtm.tm_mday <= 0) {
    		if (increment_overflow(&yourtm.tm_year, -1))
    			return WRONG;
    		i = yourtm.tm_year + (1 < yourtm.tm_mon);
    		yourtm.tm_mday += year_lengths[isleap(i)];
    	}
    	while (yourtm.tm_mday > DAYSPERLYEAR) {
    		i = yourtm.tm_year + (1 < yourtm.tm_mon);
    		yourtm.tm_mday -= year_lengths[isleap(i)];
    		if (increment_overflow(&yourtm.tm_year, 1))
    			return WRONG;
    	}
    	for ( ; ; ) {
    		i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
    		if (yourtm.tm_mday <= i)
    			break;
    		yourtm.tm_mday -= i;
    		if (++yourtm.tm_mon >= MONSPERYEAR) {
    			yourtm.tm_mon = 0;
    			if (increment_overflow(&yourtm.tm_year, 1))
    				return WRONG;
    		}
    	}
    	if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
    		return WRONG;
    	if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
    		/*
    		** We can't set tm_sec to 0, because that might push the
    		** time below the minimum representable time.
    		** Set tm_sec to 59 instead.
    		** This assumes that the minimum representable time is
    		** not in the same minute that a leap second was deleted from,
    		** which is a safer assumption than using 58 would be.
    		*/
    		if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
    			return WRONG;
    		saved_seconds = yourtm.tm_sec;
    		yourtm.tm_sec = SECSPERMIN - 1;
    	} else {
    		saved_seconds = yourtm.tm_sec;
    		yourtm.tm_sec = 0;
    	}
    	/*
    	** Divide the search space in half
    	** (this works whether time_t is signed or unsigned).
    	*/
    	bits = TYPE_BIT(time_t) - 1;
    	/*
    	** If time_t is signed, then 0 is just above the median,
    	** assuming two's complement arithmetic.
    	** If time_t is unsigned, then (1 << bits) is just above the median.
    	*/
    	t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits);
    	for ( ; ; ) {
    		(*funcp)(&t, offset, &mytm, zone);
    		dir = tmcomp(&mytm, &yourtm);
    		if (dir != 0) {
    			if (bits-- < 0)
    				return WRONG;
    			if (bits < 0)
    				--t; /* may be needed if new t is minimal */
    			else if (dir > 0)
    				t -= ((time_t) 1) << bits;
    			else	t += ((time_t) 1) << bits;
    			continue;
    		}
    		if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
    			break;
    		/*
    		** Right time, wrong type.
    		** Hunt for right time, right type.
    		** It's okay to guess wrong since the guess
    		** gets checked.
    		*/
    		/*
    		** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
    		*/
    		sp = (const struct state *)
    			(((void *) funcp == (void *) localsub) ?
    			lclptr : gmtptr);
    		if (sp == NULL)
    			return WRONG;
    		for (i = sp->typecnt - 1; i >= 0; --i) {
    			if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
    				continue;
    			for (j = sp->typecnt - 1; j >= 0; --j) {
    				if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
    					continue;
    				newt = t + sp->ttis[j].tt_gmtoff -
    					sp->ttis[i].tt_gmtoff;
    				(*funcp)(&newt, offset, &mytm, zone);
    				if (tmcomp(&mytm, &yourtm) != 0)
    					continue;
    				if (mytm.tm_isdst != yourtm.tm_isdst)
    					continue;
    				/*
    				** We have a match.
    				*/
    				t = newt;
    				goto label;
    			}
    		}
    		return WRONG;
    	}
    label:
    	newt = t + saved_seconds;
    	if ((newt < t) != (saved_seconds < 0))
    		return WRONG;
    	t = newt;
    	(*funcp)(&t, offset, tmp, zone);
    	*okayp = TRUE;
    	return t;
    }
    
    static time_t
    time1(tmp, funcp, offset, zone)
    struct tm * const	tmp;
    void (* const		funcp) P((const time_t *, long, struct tm *, const char*));
    const long		offset;
    const char * const	zone;
    {
    	register time_t			t;
    	register const struct state *	sp;
    	register int			samei, otheri;
    	int				okay;
    
    	if (tmp->tm_isdst > 1)
    		tmp->tm_isdst = 1;
    	t = time2(tmp, funcp, offset, &okay, zone);
    #ifdef PCTS
    	/*
    	** PCTS code courtesy Grant Sullivan (grant@osf.org).
    	*/
    	if (okay)
    		return t;
    	if (tmp->tm_isdst < 0)
    		tmp->tm_isdst = 0;	/* reset to std and try again */
    #endif /* defined PCTS */
    #ifndef PCTS
    	if (okay || tmp->tm_isdst < 0)
    		return t;
    #endif /* !defined PCTS */
    	/*
    	** We're supposed to assume that somebody took a time of one type
    	** and did some math on it that yielded a "struct tm" that's bad.
    	** We try to divine the type they started from and adjust to the
    	** type they need.
    	*/
    	/*
    	** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
    	*/
    	sp = (const struct state *) (((void *) funcp == (void *) localsub) ?
    		lclptr : gmtptr);
    	if (sp == NULL)
    		return WRONG;
    	for (samei = sp->typecnt - 1; samei >= 0; --samei) {
    		if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
    			continue;
    		for (otheri = sp->typecnt - 1; otheri >= 0; --otheri) {
    			if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
    				continue;
    			tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
    					sp->ttis[samei].tt_gmtoff;
    			tmp->tm_isdst = !tmp->tm_isdst;
    			t = time2(tmp, funcp, offset, &okay, zone);
    			if (okay)
    				return t;
    			tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
    					sp->ttis[samei].tt_gmtoff;
    			tmp->tm_isdst = !tmp->tm_isdst;
    		}
    	}
    	return WRONG;
    }
    
    time_t
    ast_mktime(tmp,zone)
    struct tm * const	tmp;
    const char * const	zone;
    {
    	time_t mktime_return_value;
    #ifdef	_THREAD_SAFE
    	ast_mutex_lock(&lcl_mutex);
    #endif
    	ast_tzset(zone);
    	mktime_return_value = time1(tmp, localsub, 0L, zone);
    #ifdef	_THREAD_SAFE
    	ast_mutex_unlock(&lcl_mutex);
    #endif
    	return(mktime_return_value);
    }