xref: /csrg-svn/lib/libc/gen/ctime.c (revision 13582)
1 #ifndef lint
2 static char sccsid[] = "@(#)ctime.c	4.3 (Berkeley) 07/01/83";
3 #endif
4 /*
5  * This routine converts time as follows.
6  * The epoch is 0000 Jan 1 1970 GMT.
7  * The argument time is in seconds since then.
8  * The localtime(t) entry returns a pointer to an array
9  * containing
10  *  seconds (0-59)
11  *  minutes (0-59)
12  *  hours (0-23)
13  *  day of month (1-31)
14  *  month (0-11)
15  *  year-1970
16  *  weekday (0-6, Sun is 0)
17  *  day of the year
18  *  daylight savings flag
19  *
20  * The routine calls the system to determine the local
21  * timezone and whether Daylight Saving Time is permitted locally.
22  * (DST is then determined by the current US standard rules)
23  * There is a table that accounts for the peculiarities
24  * undergone by daylight time in 1974-1975.
25  *
26  * The routine does not work
27  * in Saudi Arabia which runs on Solar time.
28  *
29  * asctime(tvec))
30  * where tvec is produced by localtime
31  * returns a ptr to a character string
32  * that has the ascii time in the form
33  *	Thu Jan 01 00:00:00 1970n0\\
34  *	01234567890123456789012345
35  *	0	  1	    2
36  *
37  * ctime(t) just calls localtime, then asctime.
38  */
39 
40 #include <sys/time.h>
41 #include <sys/types.h>
42 #include <sys/timeb.h>
43 
44 static	char	cbuf[26];
45 static	int	dmsize[12] =
46 {
47 	31,
48 	28,
49 	31,
50 	30,
51 	31,
52 	30,
53 	31,
54 	31,
55 	30,
56 	31,
57 	30,
58 	31
59 };
60 
61 /*
62  * The following table is used for 1974 and 1975 and
63  * gives the day number of the first day after the Sunday of the
64  * change.
65  */
66 struct dstab {
67 	int	dayyr;
68 	int	daylb;
69 	int	dayle;
70 };
71 
72 static struct dstab usdaytab[] = {
73 	1974,	5,	333,	/* 1974: Jan 6 - last Sun. in Nov */
74 	1975,	58,	303,	/* 1975: Last Sun. in Feb - last Sun in Oct */
75 	0,	119,	303,	/* all other years: end Apr - end Oct */
76 };
77 static struct dstab ausdaytab[] = {
78 	1970,	400,	0,	/* 1970: no daylight saving at all */
79 	1971,	303,	0,	/* 1971: daylight saving from Oct 31 */
80 	1972,	303,	58,	/* 1972: Jan 1 -> Feb 27 & Oct 31 -> dec 31 */
81 	0,	303,	65,	/* others: -> Mar 7, Oct 31 -> */
82 };
83 
84 static struct dayrules {
85 	int		dst_type;	/* number obtained from system */
86 	int		dst_hrs;	/* hours to add when dst on */
87 	struct	dstab *	dst_rules;	/* one of the above */
88 	enum {STH,NTH}	dst_hemi;	/* southern, northern hemisphere */
89 } dayrules [] = {
90 	DST_USA,	1,	usdaytab,	NTH,
91 	DST_AUST,	1,	ausdaytab,	STH,
92 	-1,
93 };
94 
95 struct tm	*gmtime();
96 char		*ct_numb();
97 struct tm	*localtime();
98 char	*ctime();
99 char	*ct_num();
100 char	*asctime();
101 
102 char *
103 ctime(t)
104 unsigned long *t;
105 {
106 	return(asctime(localtime(t)));
107 }
108 
109 struct tm *
110 localtime(tim)
111 unsigned long *tim;
112 {
113 	register int dayno;
114 	register struct tm *ct;
115 	register dalybeg, daylend;
116 	register struct dayrules *dr;
117 	register struct dstab *ds;
118 	int year;
119 	unsigned long copyt;
120 	struct timeval curtime;
121 	struct timezone zone;
122 
123 	gettimeofday(&curtime, &zone);
124 	copyt = *tim - (unsigned long)zone.tz_minuteswest*60;
125 	ct = gmtime(&copyt);
126 	dayno = ct->tm_yday;
127 	for (dr = dayrules; dr->dst_type >= 0; dr++)
128 		if (dr->dst_type == zone.tz_dsttime)
129 			break;
130 	if (dr->dst_type >= 0) {
131 		year = ct->tm_year + 1900;
132 		for (ds = dr->dst_rules; ds->dayyr; ds++)
133 			if (ds->dayyr == year)
134 				break;
135 		dalybeg = ds->daylb;	/* first Sun after dst starts */
136 		daylend = ds->dayle;	/* first Sun after dst ends */
137 		dalybeg = sunday(ct, dalybeg);
138 		daylend = sunday(ct, daylend);
139 		switch (dr->dst_hemi) {
140 		case NTH:
141 		    if (!(
142 		       (dayno>dalybeg || (dayno==dalybeg && ct->tm_hour>=2)) &&
143 		       (dayno<daylend || (dayno==daylend && ct->tm_hour<1))
144 		    ))
145 			    return(ct);
146 		    break;
147 		case STH:
148 		    if (!(
149 		       (dayno>dalybeg || (dayno==dalybeg && ct->tm_hour>=2)) ||
150 		       (dayno<daylend || (dayno==daylend && ct->tm_hour<2))
151 		    ))
152 			    return(ct);
153 		    break;
154 		default:
155 		    return(ct);
156 		}
157 	        copyt += dr->dst_hrs*60*60;
158 		ct = gmtime(&copyt);
159 		ct->tm_isdst++;
160 	}
161 	return(ct);
162 }
163 
164 /*
165  * The argument is a 0-origin day number.
166  * The value is the day number of the first
167  * Sunday on or after the day.
168  */
169 static
170 sunday(t, d)
171 register struct tm *t;
172 register int d;
173 {
174 	if (d >= 58)
175 		d += dysize(t->tm_year) - 365;
176 	return(d - (d - t->tm_yday + t->tm_wday + 700) % 7);
177 }
178 
179 struct tm *
180 gmtime(tim)
181 unsigned long *tim;
182 {
183 	register int d0, d1;
184 	unsigned long hms, day;
185 	register int *tp;
186 	static struct tm xtime;
187 
188 	/*
189 	 * break initial number into days
190 	 */
191 	hms = *tim % 86400;
192 	day = *tim / 86400;
193 	if (hms<0) {
194 		hms += 86400;
195 		day -= 1;
196 	}
197 	tp = (int *)&xtime;
198 
199 	/*
200 	 * generate hours:minutes:seconds
201 	 */
202 	*tp++ = hms%60;
203 	d1 = hms/60;
204 	*tp++ = d1%60;
205 	d1 /= 60;
206 	*tp++ = d1;
207 
208 	/*
209 	 * day is the day number.
210 	 * generate day of the week.
211 	 * The addend is 4 mod 7 (1/1/1970 was Thursday)
212 	 */
213 
214 	xtime.tm_wday = (day+7340036)%7;
215 
216 	/*
217 	 * year number
218 	 */
219 	if (day>=0) for(d1=70; day >= dysize(d1); d1++)
220 		day -= dysize(d1);
221 	else for (d1=70; day<0; d1--)
222 		day += dysize(d1-1);
223 	xtime.tm_year = d1;
224 	xtime.tm_yday = d0 = day;
225 
226 	/*
227 	 * generate month
228 	 */
229 
230 	if (dysize(d1)==366)
231 		dmsize[1] = 29;
232 	for(d1=0; d0 >= dmsize[d1]; d1++)
233 		d0 -= dmsize[d1];
234 	dmsize[1] = 28;
235 	*tp++ = d0+1;
236 	*tp++ = d1;
237 	xtime.tm_isdst = 0;
238 	return(&xtime);
239 }
240 
241 char *
242 asctime(t)
243 struct tm *t;
244 {
245 	register char *cp, *ncp;
246 	register int *tp;
247 
248 	cp = cbuf;
249 	for (ncp = "Day Mon 00 00:00:00 1900\n"; *cp++ = *ncp++;);
250 	ncp = &"SunMonTueWedThuFriSat"[3*t->tm_wday];
251 	cp = cbuf;
252 	*cp++ = *ncp++;
253 	*cp++ = *ncp++;
254 	*cp++ = *ncp++;
255 	cp++;
256 	tp = &t->tm_mon;
257 	ncp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[(*tp)*3];
258 	*cp++ = *ncp++;
259 	*cp++ = *ncp++;
260 	*cp++ = *ncp++;
261 	cp = ct_numb(cp, *--tp);
262 	cp = ct_numb(cp, *--tp+100);
263 	cp = ct_numb(cp, *--tp+100);
264 	cp = ct_numb(cp, *--tp+100);
265 	if (t->tm_year>=100) {
266 		cp[1] = '2';
267 		cp[2] = '0' + t->tm_year >= 200;
268 	}
269 	cp += 2;
270 	cp = ct_numb(cp, t->tm_year+100);
271 	return(cbuf);
272 }
273 
274 dysize(y)
275 {
276 	if((y%4) == 0)
277 		return(366);
278 	return(365);
279 }
280 
281 static char *
282 ct_numb(cp, n)
283 register char *cp;
284 {
285 	cp++;
286 	if (n>=10)
287 		*cp++ = (n/10)%10 + '0';
288 	else
289 		*cp++ = ' ';
290 	*cp++ = n%10 + '0';
291 	return(cp);
292 }
293