xref: /illumos-gate/usr/src/cmd/mdb/common/libstand/ctime.c (revision 46b592853d0f4f11781b6b0a7533f267c6aee132)
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 
23 static 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 
28 static int year_lengths[2] = {
29 	DAYS_PER_NYEAR, DAYS_PER_LYEAR
30 };
31 
32 struct tm *
33 localtime(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 
150 static 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  */
156 char *
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  */
204 char *
205 asctime_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 
214 char *
215 ctime(const time_t *t)
216 {
217 	return (asctime(localtime(t)));
218 }
219 
220 
221 char *
222 asctime(const struct tm *t)
223 {
224 	static char cbuf[CBUFSIZ];
225 
226 	return (asctime_r(t, cbuf, CBUFSIZ));
227 }
228 
229 
230 static char *
231 ct_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