1/*
2 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6/*
7 * Copyright (c) 1980 Regents of the University of California.
8 * All rights reserved. The Berkeley software License Agreement
9 * specifies the terms and conditions for redistribution.
10 */
11
12/*
13 * This localtime is a modified version of offtime from libc, which does not
14 * bother to figure out the time zone from the kernel, from environment
15 * variables, or from Unix files.
16 */
17
18#include <sys/types.h>
19#include <sys/salib.h>
20#include <tzfile.h>
21#include <errno.h>
22
23static int mon_lengths[2][MONS_PER_YEAR] = {
24	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
25	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
26};
27
28static int year_lengths[2] = {
29	DAYS_PER_NYEAR, DAYS_PER_LYEAR
30};
31
32struct tm *
33localtime(const time_t *clock)
34{
35	struct tm *tmp;
36	long days;
37	long rem;
38	int y;
39	int yleap;
40	int *ip;
41	static struct tm tm;
42
43	tmp = &tm;
44	days = *clock / SECS_PER_DAY;
45	rem = *clock % SECS_PER_DAY;
46	while (rem < 0) {
47		rem += SECS_PER_DAY;
48		--days;
49	}
50	while (rem >= SECS_PER_DAY) {
51		rem -= SECS_PER_DAY;
52		++days;
53	}
54	tmp->tm_hour = (int)(rem / SECS_PER_HOUR);
55	rem = rem % SECS_PER_HOUR;
56	tmp->tm_min = (int)(rem / SECS_PER_MIN);
57	tmp->tm_sec = (int)(rem % SECS_PER_MIN);
58	tmp->tm_wday = (int)((EPOCH_WDAY + days) % DAYS_PER_WEEK);
59	if (tmp->tm_wday < 0)
60		tmp->tm_wday += DAYS_PER_WEEK;
61	y = EPOCH_YEAR;
62	if (days >= 0) {
63		for (;;) {
64			yleap = isleap(y);
65			if (days < (long)year_lengths[yleap])
66				break;
67			if (++y > 9999) {
68				errno = EOVERFLOW;
69				return (NULL);
70			}
71			days = days - (long)year_lengths[yleap];
72		}
73	} else {
74		do {
75			if (--y < 0) {
76				errno = EOVERFLOW;
77				return (NULL);
78			}
79			yleap = isleap(y);
80			days = days + (long)year_lengths[yleap];
81		} while (days < 0);
82	}
83	tmp->tm_year = y - TM_YEAR_BASE;
84	tmp->tm_yday = (int)days;
85	ip = mon_lengths[yleap];
86	for (tmp->tm_mon = 0; days >= (long)ip[tmp->tm_mon]; ++(tmp->tm_mon))
87		days = days - (long)ip[tmp->tm_mon];
88	tmp->tm_mday = (int)(days + 1);
89	tmp->tm_isdst = 0;
90
91	return (tmp);
92}
93
94/*
95 * So is ctime...
96 */
97
98/*
99 * This routine converts time as follows.
100 * The epoch is 0000 Jan 1 1970 GMT.
101 * The argument time is in seconds since then.
102 * The localtime(t) entry returns a pointer to an array
103 * containing
104 *  seconds (0-59)
105 *  minutes (0-59)
106 *  hours (0-23)
107 *  day of month (1-31)
108 *  month (0-11)
109 *  year-1970
110 *  weekday (0-6, Sun is 0)
111 *  day of the year
112 *  daylight savings flag
113 *
114 * The routine corrects for daylight saving
115 * time and will work in any time zone provided
116 * "timezone" is adjusted to the difference between
117 * Greenwich and local standard time (measured in seconds).
118 * In places like Michigan "daylight" must
119 * be initialized to 0 to prevent the conversion
120 * to daylight time.
121 * There is a table which accounts for the peculiarities
122 * undergone by daylight time in 1974-1975.
123 *
124 * The routine does not work
125 * in Saudi Arabia which runs on Solar time.
126 *
127 * asctime(tvec)
128 * where tvec is produced by localtime
129 * returns a ptr to a character string
130 * that has the ascii time in the form
131 *	Thu Jan 01 00:00:00 1970\n\0
132 *	01234567890123456789012345
133 *	0	  1	    2
134 *
135 * ctime(t) just calls localtime, then asctime.
136 *
137 * tzset() looks for an environment variable named
138 * TZ.
139 * If the variable is present, it will set the external
140 * variables "timezone", "altzone", "daylight", and "tzname"
141 * appropriately. It is called by localtime, and
142 * may also be called explicitly by the user.
143 */
144
145
146
147#define	dysize(A) (((A)%4)? 365: 366)
148#define	CBUFSIZ 26
149
150static char *ct_numb();
151
152/*
153 * POSIX.1c standard version of the function asctime_r.
154 * User gets it via static asctime_r from the header file.
155 */
156char *
157__posix_asctime_r(const struct tm *t, char *cbuf)
158{
159	const char *Date = "Day Mon 00 00:00:00 1900\n";
160	const char *Day  = "SunMonTueWedThuFriSat";
161	const char *Month = "JanFebMarAprMayJunJulAugSepOctNovDec";
162	const char *ncp;
163	const int *tp;
164	char *cp;
165
166	if (t == NULL)
167		return (NULL);
168
169	cp = cbuf;
170	for (ncp = Date; *cp++ = *ncp++; /* */)
171		;
172	ncp = Day + (3*t->tm_wday);
173	cp = cbuf;
174	*cp++ = *ncp++;
175	*cp++ = *ncp++;
176	*cp++ = *ncp++;
177	cp++;
178	tp = &t->tm_mon;
179	ncp = Month + ((*tp) * 3);
180	*cp++ = *ncp++;
181	*cp++ = *ncp++;
182	*cp++ = *ncp++;
183	cp = ct_numb(cp, *--tp);
184	cp = ct_numb(cp, *--tp+100);
185	cp = ct_numb(cp, *--tp+100);
186	cp = ct_numb(cp, *--tp+100);
187	if (t->tm_year > 9999) {
188		errno = EOVERFLOW;
189		return (NULL);
190	} else {
191		uint_t hun = 19 + (t->tm_year / 100);
192		cp[1] = (hun / 10) + '0';
193		cp[2] = (hun % 10) + '0';
194	}
195	cp += 2;
196	cp = ct_numb(cp, t->tm_year+100);
197	return (cbuf);
198}
199
200/*
201 * POSIX.1c Draft-6 version of the function asctime_r.
202 * It was implemented by Solaris 2.3.
203 */
204char *
205asctime_r(const struct tm *t, char *cbuf, int buflen)
206{
207	if (buflen < CBUFSIZ) {
208		errno = ERANGE;
209		return (NULL);
210	}
211	return (__posix_asctime_r(t, cbuf));
212}
213
214char *
215ctime(const time_t *t)
216{
217	return (asctime(localtime(t)));
218}
219
220
221char *
222asctime(const struct tm *t)
223{
224	static char cbuf[CBUFSIZ];
225
226	return (asctime_r(t, cbuf, CBUFSIZ));
227}
228
229
230static char *
231ct_numb(char *cp, int n)
232{
233	cp++;
234	if (n >= 10)
235		*cp++ = (n/10)%10 + '0';
236	else
237		*cp++ = ' ';		/* Pad with blanks */
238	*cp++ = n%10 + '0';
239	return (cp);
240}
241