xref: /plan9-contrib/sys/src/libc/9sys/ctime.c (revision 54dec0678461a9d7af09e995e96ae173d1daa5c7)
1 /*
2  * This routine converts time as follows.
3  * The epoch is 0000 Jan 1 1970 GMT.
4  * The argument time is in seconds since then.
5  * The localtime(t) entry returns a pointer to an array
6  * containing
7  *
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 gets the daylight savings time from the environment.
19  *
20  * asctime(tvec))
21  * where tvec is produced by localtime
22  * returns a ptr to a character string
23  * that has the ascii time in the form
24  *
25  *	                            \\
26  *	Thu Jan 01 00:00:00 GMT 1970n0
27  *	012345678901234567890123456789
28  *	0	  1	    2
29  *
30  * ctime(t) just calls localtime, then asctime.
31  */
32 
33 #include <u.h>
34 #include <libc.h>
35 
36 static	char	dmsize[12] =
37 {
38 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
39 };
40 
41 /*
42  * The following table is used for 1974 and 1975 and
43  * gives the day number of the first day after the Sunday of the
44  * change.
45  */
46 
47 static	int	dysize(int);
48 static	void	ct_numb(char*, int);
49 
50 #define	TZSIZE	((136*2)+10)		/* 1970-2106; match tm2sec.c */
51 
52 static	void	readtimezone(void);
53 static	int	rd_name(char**, char*);
54 static	int	rd_long(char**, long*);
55 static
56 struct
57 {
58 	char	stname[4];
59 	char	dlname[4];
60 	long	stdiff;
61 	long	dldiff;
62 	ulong	dlpairs[TZSIZE];
63 } timezone;
64 
65 char*
ctime(long t)66 ctime(long t)
67 {
68 	return asctime(localtime(t));
69 }
70 
71 Tm*
localtime(long atim)72 localtime(long atim)		/* should be ulong, but it's too late */
73 {
74 	Tm *ct;
75 	ulong tim, t, *p;
76 	int dlflag;
77 
78 	tim = atim;
79 	if(timezone.stname[0] == 0)
80 		readtimezone();
81 	t = tim + timezone.stdiff;
82 	dlflag = 0;
83 	for(p = timezone.dlpairs; *p; p += 2)
84 		if(t >= p[0])
85 		if(t < p[1]) {
86 			t = tim + timezone.dldiff;
87 			dlflag++;
88 			break;
89 		}
90 	ct = gmtime(t);
91 	if(dlflag){
92 		strcpy(ct->zone, timezone.dlname);
93 		ct->tzoff = timezone.dldiff;
94 	} else {
95 		strcpy(ct->zone, timezone.stname);
96 		ct->tzoff = timezone.stdiff;
97 	}
98 	return ct;
99 }
100 
101 Tm*
gmtime(long atim)102 gmtime(long atim)		/* should be ulong, but it's too late */
103 {
104 	int d0, d1;
105 	long hms, day;
106 	ulong tim;
107 	static Tm xtime;
108 
109 	/*
110 	 * break initial number into days
111 	 */
112 	tim = atim;
113 	hms = tim % 86400L;
114 	day = tim / 86400L;
115 	if(hms < 0) {
116 		hms += 86400L;
117 		day -= 1;
118 	}
119 
120 	/*
121 	 * generate hours:minutes:seconds
122 	 */
123 	xtime.sec = hms % 60;
124 	d1 = hms / 60;
125 	xtime.min = d1 % 60;
126 	d1 /= 60;
127 	xtime.hour = d1;
128 
129 	/*
130 	 * day is the day number.
131 	 * generate day of the week.
132 	 * The addend is 4 mod 7 (1/1/1970 was Thursday)
133 	 */
134 
135 	xtime.wday = (day + 7340036L) % 7;
136 
137 	/*
138 	 * year number
139 	 */
140 	if(day >= 0)
141 		for(d1 = 1970; day >= dysize(d1); d1++)
142 			day -= dysize(d1);
143 	else
144 		for (d1 = 1970; day < 0; d1--)
145 			day += dysize(d1-1);
146 	xtime.year = d1-1900;
147 	xtime.yday = d0 = day;
148 
149 	/*
150 	 * generate month
151 	 */
152 
153 	if(dysize(d1) == 366)
154 		dmsize[1] = 29;
155 	for(d1 = 0; d0 >= dmsize[d1]; d1++)
156 		d0 -= dmsize[d1];
157 	dmsize[1] = 28;
158 	xtime.mday = d0 + 1;
159 	xtime.mon = d1;
160 	strcpy(xtime.zone, "GMT");
161 	return &xtime;
162 }
163 
164 char*
asctime(Tm * t)165 asctime(Tm *t)
166 {
167 	char *ncp;
168 	static char cbuf[30];
169 
170 	strcpy(cbuf, "Thu Jan 01 00:00:00 GMT 1970\n");
171 	ncp = &"SunMonTueWedThuFriSat"[t->wday*3];
172 	cbuf[0] = *ncp++;
173 	cbuf[1] = *ncp++;
174 	cbuf[2] = *ncp;
175 	ncp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[t->mon*3];
176 	cbuf[4] = *ncp++;
177 	cbuf[5] = *ncp++;
178 	cbuf[6] = *ncp;
179 	ct_numb(cbuf+8, t->mday);
180 	ct_numb(cbuf+11, t->hour+100);
181 	ct_numb(cbuf+14, t->min+100);
182 	ct_numb(cbuf+17, t->sec+100);
183 	ncp = t->zone;
184 	cbuf[20] = *ncp++;
185 	cbuf[21] = *ncp++;
186 	cbuf[22] = *ncp;
187 	if(t->year >= 100) {
188 		cbuf[24] = '2';
189 		cbuf[25] = '0';
190 	}
191 	ct_numb(cbuf+26, t->year+100);
192 	return cbuf;
193 }
194 
195 static
dysize(int y)196 dysize(int y)
197 {
198 
199 	if(y%4 == 0 && (y%100 != 0 || y%400 == 0))
200 		return 366;
201 	return 365;
202 }
203 
204 static
205 void
ct_numb(char * cp,int n)206 ct_numb(char *cp, int n)
207 {
208 
209 	cp[0] = ' ';
210 	if(n >= 10)
211 		cp[0] = (n/10)%10 + '0';
212 	cp[1] = n%10 + '0';
213 }
214 
215 static
216 void
readtimezone(void)217 readtimezone(void)
218 {
219 	char buf[TZSIZE*11+30], *p;
220 	int i;
221 
222 	memset(buf, 0, sizeof(buf));
223 	i = open("/env/timezone", 0);
224 	if(i < 0)
225 		goto error;
226 	if(read(i, buf, sizeof(buf)) >= sizeof(buf)){
227 		close(i);
228 		goto error;
229 	}
230 	close(i);
231 	p = buf;
232 	if(rd_name(&p, timezone.stname))
233 		goto error;
234 	if(rd_long(&p, &timezone.stdiff))
235 		goto error;
236 	if(rd_name(&p, timezone.dlname))
237 		goto error;
238 	if(rd_long(&p, &timezone.dldiff))
239 		goto error;
240 	for(i=0; i<TZSIZE; i++) {
241 		if(rd_long(&p, (long *)&timezone.dlpairs[i]))
242 			goto error;
243 		if(timezone.dlpairs[i] == 0)
244 			return;
245 	}
246 	/* array too small for input */
247 error:
248 	timezone.stdiff = 0;
249 	strcpy(timezone.stname, "GMT");
250 	timezone.dlpairs[0] = 0;
251 }
252 
253 static
rd_name(char ** f,char * p)254 rd_name(char **f, char *p)
255 {
256 	int c, i;
257 
258 	for(;;) {
259 		c = *(*f)++;
260 		if(c != ' ' && c != '\n')
261 			break;
262 	}
263 	for(i=0; i<3; i++) {
264 		if(c == ' ' || c == '\n')
265 			return 1;
266 		*p++ = c;
267 		c = *(*f)++;
268 	}
269 	if(c != ' ' && c != '\n')
270 		return 1;
271 	*p = 0;
272 	return 0;
273 }
274 
275 static
rd_long(char ** f,long * p)276 rd_long(char **f, long *p)
277 {
278 	int c, s;
279 	long l;
280 
281 	s = 0;
282 	for(;;) {
283 		c = *(*f)++;
284 		if(c == '-') {
285 			s++;
286 			continue;
287 		}
288 		if(c != ' ' && c != '\n')
289 			break;
290 	}
291 	if(c == 0) {
292 		*p = 0;
293 		return 0;
294 	}
295 	l = 0;
296 	for(;;) {
297 		if(c == ' ' || c == '\n')
298 			break;
299 		if(c < '0' || c > '9')
300 			return 1;
301 		l = l*10 + c-'0';
302 		c = *(*f)++;
303 	}
304 	if(s)
305 		l = -l;
306 	*p = l;
307 	return 0;
308 }
309