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