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