diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/time/__map_file.c | 20 | ||||
| -rw-r--r-- | src/time/__month_to_secs.c | 10 | ||||
| -rw-r--r-- | src/time/__secs_to_tm.c | 81 | ||||
| -rw-r--r-- | src/time/__time.h | 9 | ||||
| -rw-r--r-- | src/time/__time_to_tm.c | 83 | ||||
| -rw-r--r-- | src/time/__tm_to_secs.c | 24 | ||||
| -rw-r--r-- | src/time/__tm_to_time.c | 33 | ||||
| -rw-r--r-- | src/time/__tz.c | 389 | ||||
| -rw-r--r-- | src/time/__year_to_secs.c | 47 | ||||
| -rw-r--r-- | src/time/gmtime.c | 9 | ||||
| -rw-r--r-- | src/time/gmtime_r.c | 21 | ||||
| -rw-r--r-- | src/time/localtime.c | 9 | ||||
| -rw-r--r-- | src/time/localtime_r.c | 20 | ||||
| -rw-r--r-- | src/time/mktime.c | 36 | ||||
| -rw-r--r-- | src/time/strftime.c | 8 | ||||
| -rw-r--r-- | src/time/time_impl.h | 9 | ||||
| -rw-r--r-- | src/time/timegm.c | 17 | ||||
| -rw-r--r-- | src/time/tzset.c | 172 | 
18 files changed, 649 insertions, 348 deletions
| diff --git a/src/time/__map_file.c b/src/time/__map_file.c new file mode 100644 index 00000000..b6bf272a --- /dev/null +++ b/src/time/__map_file.c @@ -0,0 +1,20 @@ +#include <sys/mman.h> +#include <fcntl.h> +#include <sys/stat.h> +#include "syscall.h" + +void *__mmap(void *, size_t, int, int, int, off_t); + +const char unsigned *__map_file(const char *pathname, size_t *size) +{ +	struct stat st; +	const unsigned char *map = MAP_FAILED; +	int flags = O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK; +	int fd = __syscall(SYS_open, pathname, flags); +	if (fd < 0) return 0; +	if (!__syscall(SYS_fstat, fd, &st)) +		map = __mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); +	__syscall(SYS_close); +	*size = st.st_size; +	return map == MAP_FAILED ? 0 : map; +} diff --git a/src/time/__month_to_secs.c b/src/time/__month_to_secs.c new file mode 100644 index 00000000..43248fb3 --- /dev/null +++ b/src/time/__month_to_secs.c @@ -0,0 +1,10 @@ +int __month_to_secs(int month, int is_leap) +{ +	static const int secs_through_month[] = { +		0, 31*86400, 59*86400, 90*86400, +		120*86400, 151*86400, 181*86400, 212*86400, +		243*86400, 273*86400, 304*86400, 334*86400 }; +	int t = secs_through_month[month]; +	if (is_leap && month >= 2) t+=86400; +	return t; +} diff --git a/src/time/__secs_to_tm.c b/src/time/__secs_to_tm.c new file mode 100644 index 00000000..f3c1cf92 --- /dev/null +++ b/src/time/__secs_to_tm.c @@ -0,0 +1,81 @@ +#include "time_impl.h" +#include <limits.h> + +/* 2000-03-01 (mod 400 year, immediately after feb29 */ +#define LEAPOCH (946684800LL + 86400*(31+29)) + +#define DAYS_PER_400Y (365*400 + 97) +#define DAYS_PER_100Y (365*100 + 24) +#define DAYS_PER_4Y   (365*4   + 1) + +int __secs_to_tm(long long t, struct tm *tm) +{ +	long long days, secs; +	int remdays, remsecs, remyears; +	int qc_cycles, c_cycles, q_cycles; +	int years, months; +	int wday, yday, leap; +	static const char days_in_month[] = {31,30,31,30,31,31,30,31,30,31,31,29}; + +	/* Reject time_t values whose year would overflow int */ +	if (t < INT_MIN * 31622400LL || t > INT_MAX * 31622400LL) +		return -1; + +	secs = t - LEAPOCH; +	days = secs / 86400; +	remsecs = secs % 86400; +	if (remsecs < 0) { +		remsecs += 86400; +		days--; +	} + +	wday = (3+days)%7; +	if (wday < 0) wday += 7; + +	qc_cycles = days / DAYS_PER_400Y; +	remdays = days % DAYS_PER_400Y; +	if (remdays < 0) { +		remdays += DAYS_PER_400Y; +		qc_cycles--; +	} + +	c_cycles = remdays / DAYS_PER_100Y; +	if (c_cycles == 4) c_cycles--; +	remdays -= c_cycles * DAYS_PER_100Y; + +	q_cycles = remdays / DAYS_PER_4Y; +	if (q_cycles == 25) q_cycles--; +	remdays -= q_cycles * DAYS_PER_4Y; + +	remyears = remdays / 365; +	if (remyears == 4) remyears--; +	remdays -= remyears * 365; + +	leap = !remyears && (q_cycles || !c_cycles); +	yday = remdays + 31 + 28 + leap; +	if (yday >= 365+leap) yday -= 365+leap; + +	years = remyears + 4*q_cycles + 100*c_cycles + 400*qc_cycles; + +	for (months=0; days_in_month[months] <= remdays; months++) +		remdays -= days_in_month[months]; + +	if (years+100 > INT_MAX || years+100 < INT_MIN) +		return -1; + +	tm->tm_year = years + 100; +	tm->tm_mon = months + 2; +	if (tm->tm_mon >= 12) { +		tm->tm_mon -=12; +		tm->tm_year++; +	} +	tm->tm_mday = remdays + 1; +	tm->tm_wday = wday; +	tm->tm_yday = yday; + +	tm->tm_hour = remsecs / 3600; +	tm->tm_min = remsecs / 60 % 60; +	tm->tm_sec = remsecs % 60; + +	return 0; +} diff --git a/src/time/__time.h b/src/time/__time.h deleted file mode 100644 index 967e5180..00000000 --- a/src/time/__time.h +++ /dev/null @@ -1,9 +0,0 @@ -time_t __tm_to_time(struct tm *); -struct tm *__time_to_tm(time_t, struct tm *); -void __tzset(void); -struct tm *__dst_adjust(struct tm *tm); - -extern long __timezone; -extern int __daylight; -extern int __dst_offset; -extern char *__tzname[2]; diff --git a/src/time/__time_to_tm.c b/src/time/__time_to_tm.c deleted file mode 100644 index e2d782be..00000000 --- a/src/time/__time_to_tm.c +++ /dev/null @@ -1,83 +0,0 @@ -#include <time.h> - -/* C defines the rounding for division in a nonsensical way */ -#define Q(a,b) ((a)>0 ? (a)/(b) : -(((b)-(a)-1)/(b))) - -#define DAYS_PER_400Y (365*400 + 97) -#define DAYS_PER_100Y (365*100 + 24) -#define DAYS_PER_4Y   (365*4   + 1) - -/* FIXME: use lldiv once it's fixed to compute quot,rem together */ -struct tm *__time_to_tm(time_t t, struct tm *tm) -{ -	/* months are march-based */ -	static const int days_thru_month[] = {31,61,92,122,153,184,214,245,275,306,337,366}; -	long long bigday; -	unsigned int day, year4, year100; -	int year, year400; -	int month; -	int leap; -	int hour, min, sec; -	int wday, mday, yday; - -	/* start from 2000-03-01 (multiple of 400 years) */ -	t += -946684800 - 86400*(31+29); - -	bigday = Q(t, 86400); -	sec = t-bigday*86400; - -	hour = sec/3600; -	sec -= hour*3600; -	min = sec/60; -	sec -= min*60; - -	/* 2000-03-01 was a wednesday */ -	wday = (3+bigday)%7; -	if (wday < 0) wday += 7; - -	t = -946684800LL - 86400*(31+29) + 9000000; -	 -	year400 = Q(bigday, DAYS_PER_400Y); -	day = bigday-year400*DAYS_PER_400Y; - -	year100 = day/DAYS_PER_100Y; -	if (year100 == 4) year100--; -	day -= year100*DAYS_PER_100Y; - -	year4 = day/DAYS_PER_4Y; -	if (year4 == 25) year4--; -	day -= year4*DAYS_PER_4Y; - -	year = day/365; -	if (year == 4) year--; -	day -= year*365; - -	leap = !year && (year4 || !year100); -	yday = day + 31+28 + leap; -	if (yday >= 365+leap) yday -= 365+leap; - -	year += 4*year4 + 100*year100 + 400*year400 + 2000-1900; - -	for (month=0; days_thru_month[month] <= day; month++); -	if (month) day -= days_thru_month[month-1]; -	month += 2; -	if (month >= 12) { -		month -= 12; -		year++; -	} - -	mday = day+1; - -	tm->tm_sec = sec; -	tm->tm_min = min; -	tm->tm_hour= hour; -	tm->tm_mday= mday; -	tm->tm_mon = month; -	tm->tm_year= year; -	tm->tm_wday= wday; -	tm->tm_yday= yday; -	tm->__tm_zone = 0; -	tm->__tm_gmtoff = 0; - -	return tm; -} diff --git a/src/time/__tm_to_secs.c b/src/time/__tm_to_secs.c new file mode 100644 index 00000000..c29fa985 --- /dev/null +++ b/src/time/__tm_to_secs.c @@ -0,0 +1,24 @@ +#include "time_impl.h" + +long long __tm_to_secs(const struct tm *tm) +{ +	int is_leap; +	long long year = tm->tm_year; +	int month = tm->tm_mon; +	if (month >= 12 || month < 0) { +		int adj = month / 12; +		month %= 12; +		if (month < 0) { +			adj--; +			month += 12; +		} +		year += adj; +	} +	long long t = __year_to_secs(year, &is_leap); +	t += __month_to_secs(month, is_leap); +	t += 86400LL * (tm->tm_mday-1); +	t += 3600LL * tm->tm_hour; +	t += 60LL * tm->tm_min; +	t += tm->tm_sec; +	return t; +} diff --git a/src/time/__tm_to_time.c b/src/time/__tm_to_time.c deleted file mode 100644 index 9f11805d..00000000 --- a/src/time/__tm_to_time.c +++ /dev/null @@ -1,33 +0,0 @@ -#include <time.h> - -/* C defines the rounding for division in a nonsensical way */ -#define Q(a,b) ((a)>0 ? (a)/(b) : -(((b)-(a)-1)/(b))) - -time_t __tm_to_time(struct tm *tm) -{ -	time_t year  = tm->tm_year + -100; -	int    month = tm->tm_mon; -	int    day   = tm->tm_mday; -	int z4, z100, z400; - -	/* normalize month */ -	if (month >= 12) { -		year += month/12; -		month %= 12; -	} else if (month < 0) { -		year += month/12; -		month %= 12; -		if (month) { -			month += 12; -			year--; -		} -	} -	z4 = Q(year - (month < 2), 4); -	z100 = Q(z4, 25); -	z400 = Q(z100, 4); -	day += year*365 + z4 - z100 + z400 + -		month[(const int []){0,31,59,90,120,151,181,212,243,273,304,334}]; -	return (long long)day*86400 -		+ tm->tm_hour*3600 + tm->tm_min*60 + tm->tm_sec -		- -946684800; /* the dawn of time :) */ -} diff --git a/src/time/__tz.c b/src/time/__tz.c new file mode 100644 index 00000000..a76a7b48 --- /dev/null +++ b/src/time/__tz.c @@ -0,0 +1,389 @@ +#include "time_impl.h" +#include <stdint.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include "libc.h" + +long  __timezone = 0; +int   __daylight = 0; +char *__tzname[2] = { 0, 0 }; + +weak_alias(__timezone, timezone); +weak_alias(__daylight, daylight); +weak_alias(__tzname, tzname); + +static char std_name[TZNAME_MAX+1]; +static char dst_name[TZNAME_MAX+1]; + +static int dst_off; +static int r0[5], r1[5]; + +static const unsigned char *zi, *trans, *index, *types, *abbrevs; +static size_t map_size; + +static char old_tz_buf[32]; +static char *old_tz = old_tz_buf; +static size_t old_tz_size = sizeof old_tz_buf; + +static int lock[2]; + +static int getint(const char **p) +{ +	unsigned x; +	for (x=0; **p-'0'<10U; (*p)++) x = **p-'0' + 10*x; +	return x; +} + +static int getsigned(const char **p) +{ +	if (**p == '-') { +		++*p; +		return -getint(p); +	} +	if (**p == '+') ++*p; +	return getint(p); +} + +static int getoff(const char **p) +{ +	int off = 3600*getsigned(p); +	if (**p == ':') { +		++*p; +		off += 60*getint(p); +		if (**p == ':') { +			++*p; +			off += getint(p); +		} +	} +	return off; +} + +static void getrule(const char **p, int rule[5]) +{ +	int r = rule[0] = **p; + +	if (r!='M') { +		if (r=='J') ++*p; +		else rule[0] = 0; +		rule[1] = getint(p); +	} else { +		++*p; rule[1] = getint(p); +		++*p; rule[2] = getint(p); +		++*p; rule[3] = getint(p); +	} + +	if (**p=='/') { +		++*p; +		rule[4] = getoff(p); +	} else { +		rule[4] = 7200; +	} +} + +static void getname(char *d, const char **p) +{ +	int i; +	if (**p == '<') { +		++*p; +		for (i=0; **p!='>' && i<TZNAME_MAX; i++) +			d[i] = (*p)[i]; +		++*p; +	} else { +		for (i=0; ((*p)[i]|32)-'a'<26U && i<TZNAME_MAX; i++) +			d[i] = (*p)[i]; +	} +	*p += i; +	d[i] = 0; +} + +#define VEC(...) ((const unsigned char[]){__VA_ARGS__}) + +static uint32_t zi_read32(const unsigned char *z) +{ +	return (unsigned)z[0]<<24 | z[1]<<16 | z[2]<<8 | z[3]; +} + +static size_t zi_dotprod(const unsigned char *z, const unsigned char *v, size_t n) +{ +	size_t y; +	uint32_t x; +	for (y=0; n; n--, z+=4, v++) { +		x = zi_read32(z); +		y += x * *v; +	} +	return y; +} + +int __munmap(void *, size_t); + +static void do_tzset() +{ +	char buf[NAME_MAX+25], *pathname=buf+24; +	const char *try, *s; +	const unsigned char *map = 0; +	size_t i; +	static const char search[] = +		"/usr/share/zoneinfo/\0/share/zoneinfo/\0/etc/zoneinfo/\0"; + +	s = getenv("TZ"); +	if (!s) s = ""; + +	if (old_tz && !strcmp(s, old_tz)) return; + +	if (zi) __munmap((void *)zi, map_size); + +	/* Cache the old value of TZ to check if it has changed. Avoid +	 * free so as not to pull it into static programs. Growth +	 * strategy makes it so free would have minimal benefit anyway. */ +	i = strlen(s); +	if (i > PATH_MAX+1) s = "", i = 0; +	if (i >= old_tz_size) { +		old_tz_size *= 2; +		if (i >= old_tz_size) old_tz_size = i+1; +		if (old_tz_size > PATH_MAX+2) old_tz_size = PATH_MAX+2; +		old_tz = malloc(old_tz_size); +	} +	if (old_tz) memcpy(old_tz, s, i+1); + +	if (*s == ':') s++; + +	/* Non-suid can use an absolute tzfile pathname or a relative +	 * pathame beginning with "."; in secure mode, only the +	 * standard path will be searched. */ +	if (*s == '/' || *s == '.') { +		if (!libc.secure) map = __map_file(s, &map_size); +	} else { +		for (i=0; s[i] && s[i]!=','; i++) { +			if (s[i]=='/') { +				size_t l = strlen(s); +				if (l > NAME_MAX || strchr(s, '.')) +					break; +				memcpy(pathname, s, l+1); +				pathname[l] = 0; +				for (try=search; !map && *try; try+=l) { +					l = strlen(try); +					memcpy(pathname-l, try, l); +					map = __map_file(pathname-l, &map_size); +				} +				break; +			} +		} +	} + +	zi = map; +	if (map) { +		int scale = 2; +		if (sizeof(time_t) > 4 && map[4]=='2') { +			size_t skip = zi_dotprod(zi, VEC(1,1,8,5,6,1), 6); +			trans = zi+skip+44+20; +			scale++; +		} else { +			trans = zi+44; +		} +		index = trans + (zi_read32(trans-12) << scale); +		types = index + zi_read32(trans-12); +		abbrevs = types + 6*zi_read32(trans-8); +		if (zi[map_size-1] == '\n') { +			for (s = (const char *)zi+map_size-2; *s!='\n'; s--); +			s++; +		} else { +			s = 0; +		} +	} + +	if (!s) s = "GMT0"; +	getname(std_name, &s); +	__tzname[0] = std_name; +	__timezone = getoff(&s); +	getname(dst_name, &s); +	__tzname[1] = dst_name; +	if (dst_name[0]) { +		__daylight = 1; +		if (*s == '+' || *s=='-' || *s-'0'<10U) +			dst_off = getoff(&s); +		else +			dst_off = __timezone - 3600; +	} else { +		__daylight = 0; +		dst_off = 0; +	} + +	if (*s == ',') s++, getrule(&s, r0); +	if (*s == ',') s++, getrule(&s, r1); +} + +/* Search zoneinfo rules to find the one that applies to the given time, + * and determine alternate opposite-DST-status rule that may be needed. */ + +static size_t scan_trans(long long t, int local, size_t *alt) +{ +	int scale = 3 - (trans == zi+44); +	uint64_t x; +	int off = 0; + +	size_t a = 0, n = (index-trans)>>scale, m; + +	if (!n) { +		if (alt) *alt = 0; +		return 0; +	} + +	/* Binary search for 'most-recent rule before t'. */ +	while (n > 1) { +		m = a + n/2; +		x = zi_read32(trans + (m<<scale)); +		if (scale == 3) x = x<<32 | zi_read32(trans + (m<<scale) + 4); +		else x = (int32_t)x; +		if (local) off = (int32_t)zi_read32(types + 6 * index[m-1]); +		if (t - off < (int64_t)x) { +			n /= 2; +		} else { +			a = m; +			n -= n/2; +		} +	} + +	/* First and last entry are special. First means to use lowest-index +	 * non-DST type. Last means to apply POSIX-style rule if available. */ +	n = (index-trans)>>scale; +	if (a == n-1) return -1; +	if (a == 0) { +		x = zi_read32(trans + (a<<scale)); +		if (scale == 3) x = x<<32 | zi_read32(trans + (a<<scale) + 4); +		else x = (int32_t)x; +		if (local) off = (int32_t)zi_read32(types + 6 * index[a-1]); +		if (t - off < (int64_t)x) { +			for (a=0; a<(abbrevs-types)/6; a++) { +				if (types[6*a+4] != types[4]) break; +			} +			if (a == (abbrevs-types)/6) a = 0; +			if (types[6*a+4]) { +				*alt = a; +				return 0; +			} else { +				*alt = 0; +				return a; +			} +		} +	} + +	/* Try to find a neighboring opposite-DST-status rule. */ +	if (alt) { +		if (a && types[6*index[a-1]+4] != types[6*index[a]+4]) +			*alt = index[a-1]; +		else if (a+1<n && types[6*index[a+1]+4] != types[6*index[a]+4]) +			*alt = index[a+1]; +		else +			*alt = index[a]; +	} + +	return index[a]; +} + +static int days_in_month(int m, int is_leap) +{ +	if (m==2) return 28+is_leap; +	else return 30+((0xad5>>(m-1))&1); +} + +/* Convert a POSIX DST rule plus year to seconds since epoch. */ + +static long long rule_to_secs(const int *rule, int year) +{ +	int is_leap; +	long long t = __year_to_secs(year, &is_leap); +	int x, m, n, d; +	if (rule[0]!='M') { +		x = rule[1]; +		if (rule[0]=='J' && (x < 60 || !is_leap)) x--; +		t += 86400 * x; +	} else { +		m = rule[1]; +		n = rule[2]; +		d = rule[3]; +		t += __month_to_secs(m-1, is_leap); +		int wday = (int)((t + 4*86400) % (7*86400)) / 86400; +		int days = d - wday; +		if (days < 0) days += 7; +		if (n == 5 && days+28 >= days_in_month(m, is_leap)) n = 4; +		t += 86400 * (days + 7*(n-1)); +	} +	t += rule[4]; +	return t; +} + +/* Determine the time zone in effect for a given time in seconds since the + * epoch. It can be given in local or universal time. The results will + * indicate whether DST is in effect at the queried time, and will give both + * the GMT offset for the active zone/DST rule and the opposite DST. This + * enables a caller to efficiently adjust for the case where an explicit + * DST specification mismatches what would be in effect at the time. */ + +void __secs_to_zone(long long t, int local, int *isdst, long *offset, long *oppoff, const char **zonename) +{ +	LOCK(lock); + +	do_tzset(); + +	if (zi) { +		size_t alt, i = scan_trans(t, local, &alt); +		if (i != -1) { +			*isdst = types[6*i+4]; +			*offset = -(int32_t)zi_read32(types+6*i); +			*zonename = (const char *)abbrevs + types[6*i+5]; +			if (oppoff) *oppoff = -(int32_t)zi_read32(types+6*alt); +			UNLOCK(lock); +			return; +		} +	} + +	if (!__daylight) goto std; + +	/* FIXME: may be broken if DST changes right at year boundary? +	 * Also, this could be more efficient.*/ +	long long y = t / 31556952 + 70; +	while (__year_to_secs(y, 0) > t) y--; +	while (__year_to_secs(y+1, 0) < t) y++; + +	long long t0 = rule_to_secs(r0, y); +	long long t1 = rule_to_secs(r1, y); + +	if (t0 < t1) { +		if (!local) { +			t0 += __timezone; +			t1 += dst_off; +		} +		if (t >= t0 && t < t1) goto dst; +		goto std; +	} else { +		if (!local) { +			t1 += __timezone; +			t0 += dst_off; +		} +		if (t >= t1 && t < t0) goto std; +		goto dst; +	} +std: +	*isdst = 0; +	*offset = __timezone; +	if (oppoff) *oppoff = dst_off; +	*zonename = __tzname[0]; +	UNLOCK(lock); +	return; +dst: +	*isdst = 1; +	*offset = dst_off; +	if (oppoff) *oppoff = __timezone; +	*zonename = __tzname[1]; +	UNLOCK(lock); +} + +void __tzset() +{ +	LOCK(lock); +	do_tzset(); +	UNLOCK(lock); +} + +weak_alias(__tzset, tzset); diff --git a/src/time/__year_to_secs.c b/src/time/__year_to_secs.c new file mode 100644 index 00000000..2824ec6d --- /dev/null +++ b/src/time/__year_to_secs.c @@ -0,0 +1,47 @@ +long long __year_to_secs(long long year, int *is_leap) +{ +	if (year-2ULL <= 136) { +		int y = year; +		int leaps = (y-68)>>2; +		if (!((y-68)&3)) { +			leaps--; +			if (is_leap) *is_leap = 1; +		} else if (is_leap) *is_leap = 0; +		return 31536000*(y-70) + 86400*leaps; +	} + +	int cycles, centuries, leaps, rem; + +	if (!is_leap) is_leap = &(int){0}; +	cycles = (year-100) / 400; +	rem = (year-100) % 400; +	if (rem < 0) { +		cycles--; +		rem += 400; +	} +	if (!rem) { +		*is_leap = 1; +		centuries = 0; +		leaps = 0; +	} else { +		if (rem >= 200) { +			if (rem >= 300) centuries = 3, rem -= 300; +			else centuries = 2, rem -= 200; +		} else { +			if (rem >= 100) centuries = 1, rem -= 100; +			else centuries = 0; +		} +		if (!rem) { +			*is_leap = 0; +			leaps = 0; +		} else { +			leaps = rem / 4U; +			rem %= 4U; +			*is_leap = !rem; +		} +	} + +	leaps += 97*cycles + 24*centuries - *is_leap; + +	return (year-100) * 31536000LL + leaps * 86400LL + 946684800 + 86400; +} diff --git a/src/time/gmtime.c b/src/time/gmtime.c index d4d5d1f1..3791b24c 100644 --- a/src/time/gmtime.c +++ b/src/time/gmtime.c @@ -1,11 +1,10 @@ -#include <time.h> +#include "time_impl.h" +#include <errno.h> -#include "__time.h" +struct tm *__gmtime_r(const time_t *restrict, struct tm *restrict);  struct tm *gmtime(const time_t *t)  {  	static struct tm tm; -	__time_to_tm(*t, &tm); -	tm.tm_isdst = 0; -	return &tm; +	return __gmtime_r(t, &tm);  } diff --git a/src/time/gmtime_r.c b/src/time/gmtime_r.c index 13a2548f..0272870d 100644 --- a/src/time/gmtime_r.c +++ b/src/time/gmtime_r.c @@ -1,10 +1,17 @@ -#include <time.h> +#include "time_impl.h" +#include <errno.h> +#include "libc.h" -#include "__time.h" - -struct tm *gmtime_r(const time_t *restrict t, struct tm *restrict result) +struct tm *__gmtime_r(const time_t *restrict t, struct tm *restrict tm)  { -	__time_to_tm(*t, result); -	result->tm_isdst = 0; -	return result; +	if (__secs_to_tm(*t, tm) < 0) { +		errno = EINVAL; +		return 0; +	} +	tm->tm_isdst = 0; +	tm->__tm_gmtoff = 0; +	tm->__tm_zone = "GMT"; +	return tm;  } + +weak_alias(__gmtime_r, gmtime_r); diff --git a/src/time/localtime.c b/src/time/localtime.c index abd5e84d..bb6718c3 100644 --- a/src/time/localtime.c +++ b/src/time/localtime.c @@ -1,12 +1,9 @@ -#include <time.h> +#include "time_impl.h" -#include "__time.h" +struct tm *__localtime_r(const time_t *restrict, struct tm *restrict);  struct tm *localtime(const time_t *t)  {  	static struct tm tm; -	__tzset(); -	__time_to_tm(*t - __timezone, &tm); -	tm.tm_isdst = -1; -	return __dst_adjust(&tm); +	return __localtime_r(t, &tm);  } diff --git a/src/time/localtime_r.c b/src/time/localtime_r.c index 389a5917..b36c1d82 100644 --- a/src/time/localtime_r.c +++ b/src/time/localtime_r.c @@ -1,11 +1,15 @@ -#include <time.h> +#include "time_impl.h" +#include <errno.h> +#include "libc.h" -#include "__time.h" - -struct tm *localtime_r(const time_t *restrict t, struct tm *restrict result) +struct tm *__localtime_r(const time_t *restrict t, struct tm *restrict tm)  { -	__tzset(); -	__time_to_tm(*t - __timezone, result); -	result->tm_isdst = -1; -	return __dst_adjust(result); +	__secs_to_zone(*t, 0, &tm->tm_isdst, &tm->__tm_gmtoff, 0, &tm->__tm_zone); +	if (__secs_to_tm((long long)*t - tm->__tm_gmtoff, tm) < 0) { +		errno = EINVAL; +		return 0; +	} +	return tm;  } + +weak_alias(__localtime_r, localtime_r); diff --git a/src/time/mktime.c b/src/time/mktime.c index 858cd50d..e38b4619 100644 --- a/src/time/mktime.c +++ b/src/time/mktime.c @@ -1,24 +1,30 @@ -#include <time.h> - -#include "__time.h" +#include "time_impl.h" +#include <errno.h> +#include <stdlib.h> +#include <string.h>  time_t mktime(struct tm *tm)  { -	int isdst = tm->tm_isdst; -	time_t t, lt; +	struct tm new; +	long opp; +	long long t = __tm_to_secs(tm); + +	__secs_to_zone(t, 1, &new.tm_isdst, &new.__tm_gmtoff, &opp, &new.__tm_zone); -	__tzset(); +	if (tm->tm_isdst>=0 && new.tm_isdst!=tm->tm_isdst) +		t += opp - new.__tm_gmtoff; -	tm->tm_sec += __timezone; -	if (isdst > 0) tm->tm_sec += __dst_offset; +	t += new.__tm_gmtoff; +	if ((time_t)t != t) goto error; -	t = __tm_to_time(tm); -	 -	lt = t - __timezone; -	if (isdst > 0) lt -= __dst_offset; -	__time_to_tm(lt, tm); +	__secs_to_zone(t, 0, &new.tm_isdst, &new.__tm_gmtoff, &opp, &new.__tm_zone); -	__dst_adjust(tm); -	 +	if (__secs_to_tm(t - new.__tm_gmtoff, &new) < 0) goto error; + +	*tm = new;  	return t; + +error: +	errno = EINVAL; +	return -1;  } diff --git a/src/time/strftime.c b/src/time/strftime.c index 57687058..d16e8134 100644 --- a/src/time/strftime.c +++ b/src/time/strftime.c @@ -3,7 +3,6 @@  #include <langinfo.h>  #include <time.h>  #include <limits.h> -#include "__time.h"  // FIXME: integer overflows @@ -182,14 +181,11 @@ do_fmt:  			fmt = "%04d";  			goto number;  		case 'z': -			if (tm->tm_isdst < 0) continue; -			val = -__timezone - (tm->tm_isdst ? __dst_offset : 0); +			val = -tm->__tm_gmtoff;  			l += snprintf(s+l, n-l, "%+.2d%.2d", val/3600, abs(val%3600)/60);  			continue;  		case 'Z': -			if (tm->tm_isdst < 0 || !__tzname[0] || !__tzname[0][0]) -				continue; -			l += snprintf(s+l, n-l, "%s", __tzname[!!tm->tm_isdst]); +			l += snprintf(s+l, n-l, "%s", tm->__tm_zone);  			continue;  		default:  			return 0; diff --git a/src/time/time_impl.h b/src/time/time_impl.h new file mode 100644 index 00000000..2e9a2c09 --- /dev/null +++ b/src/time/time_impl.h @@ -0,0 +1,9 @@ +#include <time.h> + +int __days_in_month(int, int); +int __month_to_secs(int, int); +long long __year_to_secs(long long, int *); +long long __tm_to_secs(const struct tm *); +int __secs_to_tm(long long, struct tm *); +void __secs_to_zone(long long, int, int *, long *, long *, const char **); +const unsigned char *__map_file(const char *, size_t *); diff --git a/src/time/timegm.c b/src/time/timegm.c index 6d08917e..7148fca3 100644 --- a/src/time/timegm.c +++ b/src/time/timegm.c @@ -1,9 +1,18 @@  #define _GNU_SOURCE -#include <time.h> - -#include "__time.h" +#include "time_impl.h" +#include <errno.h>  time_t timegm(struct tm *tm)  { -	return __tm_to_time(tm); +	struct tm new; +	long long t = __tm_to_secs(tm); +	if (__secs_to_tm(t, &new) < 0) { +		errno = EINVAL; +		return -1; +	} +	*tm = new; +	tm->tm_isdst = 0; +	tm->__tm_gmtoff = 0; +	tm->__tm_zone = "GMT"; +	return t;  } diff --git a/src/time/tzset.c b/src/time/tzset.c deleted file mode 100644 index 7e836c2f..00000000 --- a/src/time/tzset.c +++ /dev/null @@ -1,172 +0,0 @@ -#include <time.h> -#include <ctype.h> -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include "libc.h" - -#include "__time.h" - -long  __timezone = 0; -int   __daylight = 0; -char *__tzname[2] = { 0, 0 }; -int   __dst_offset = 0; - -weak_alias(__timezone, timezone); -weak_alias(__daylight, daylight); -weak_alias(__tzname, tzname); - -static char std_name[TZNAME_MAX+1]; -static char dst_name[TZNAME_MAX+1]; - -/* all elements are zero-based */ -static struct rule { -	signed char month; -	signed char week; -	short day; -	int time; -} __dst_start, __dst_end; - -static void zname(char *d, char **s) -{ -	int i; -	for (i=0; i<TZNAME_MAX && isalpha(d[i]=**s); i++, (*s)++); -	d[i] = 0; -} - -static int hhmmss(char **s) -{ -	int ofs = strtol(*s, s, 10)*3600; -	if (ofs >= 0) { -		if (**s == ':') ofs += strtol(*s+1, s, 10)*60; -		if (**s == ':') ofs += strtol(*s+1, s, 10); -	} else { -		if (**s == ':') ofs -= strtol(*s+1, s, 10)*60; -		if (**s == ':') ofs -= strtol(*s+1, s, 10); -	} -	return ofs; -} - -static int dstrule(struct rule *rule, char **s) -{ -	if (**s != ',') return -1; -	switch (*++*s) { -	case 'J': -		rule->month = 'J'; -		rule->day = strtol(*s+1, s, 10)-1; -		break; -	case 'M': -		rule->month = strtol(*s+1, s, 10)-1; -		if (**s != '.' || rule->month < 0 || rule->month > 11) -			return -1; -		rule->week = strtol(*s+1, s, 10)-1; -		if (**s != '.' || rule->week < 0 || rule->week > 4) -			return -1; -		rule->day = strtol(*s+1, s, 10); -		if (rule->day < 0 || rule->day > 6) -			return -1; -		break; -	default: -		rule->month = 'L'; -		rule->day = strtol(*s+1, s, 10); -		break; -	} -	if (**s == '/') { -		(*s)++; -		rule->time = hhmmss(s); -	} else rule->time = 7200; -	return 0; -} - -void tzset(void) -{ -	char *z, *a; -	 -	strcpy(std_name, "GMT"); -	strcpy(dst_name, "GMT"); -	__tzname[0] = std_name; -	__tzname[1] = dst_name; -	__timezone = 0; -	__daylight = 0; -	 -	if (!(z = getenv("TZ")) || !isalpha(*z)) return; - -	zname(std_name, &z); -	__timezone = hhmmss(&z); - -	zname(dst_name, &z); -	if (dst_name[0]) __daylight=1; -	a = z; -	__dst_offset = hhmmss(&z) - __timezone; -	if (z==a) __dst_offset = -3600; - -	if (dstrule(&__dst_start, &z) || dstrule(&__dst_end, &z)) -		__daylight = 0; -} - -void __tzset(void) -{ -	static int lock[2], init; -	if (init) return; -	LOCK(lock); -	if (!init) tzset(); -	init=1; -	UNLOCK(lock); -} - -static int is_leap(int year) -{ -	year -= 100; -	return !(year&3) && ((year%100) || !(year%400)); -} - -static int cutoff_yday(struct tm *tm, struct rule *rule) -{ -	static const char days_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31}; -	static const int first_day[] = {0,31,59,90,120,151,181,212,243,273,304,335}; -	int yday, mday, leap; -	 -	switch (rule->month) { -	case 'J': -		return rule->day + (tm->tm_mon > 1 && is_leap(tm->tm_year)); -	case 'L': -		return rule->day; -	default: -		yday = first_day[rule->month]; -		leap = is_leap(tm->tm_year); -		if (rule->month > 1 && leap) yday++; -		mday = (rule->day - (yday + tm->tm_wday - tm->tm_yday) + 1400)%7 + 7*rule->week; -		if (mday >= days_in_month[rule->month] + (leap && rule->month == 1)) -			mday -= 7; -		return mday + yday; -	} -} - -struct tm *__dst_adjust(struct tm *tm) -{ -	time_t t; -	int start, end, secs; -	int after_start, before_end; - -	if (tm->tm_isdst >= 0) return tm; -	if (!__daylight) { -		tm->tm_isdst = 0; -		return tm; -	} -	 -	secs = tm->tm_hour*3600 + tm->tm_min*60 + tm->tm_sec; -	start = cutoff_yday(tm, &__dst_start); -	end = cutoff_yday(tm, &__dst_end); - -	after_start = (tm->tm_yday > start || (tm->tm_yday == start && secs >= __dst_start.time)); -	before_end = (tm->tm_yday < end || (tm->tm_yday == end && secs < __dst_end.time)); - -	if ((after_start && before_end) || ((end < start) && (after_start || before_end))) { -		tm->tm_sec -= __dst_offset; -		tm->tm_isdst = 1; -		t = __tm_to_time(tm); -		return __time_to_tm(t, tm); -	} else tm->tm_isdst = 0; - -	return tm; -} | 
