xref: /plan9-contrib/sys/src/libc/9sys/tm2sec.c (revision 54dec0678461a9d7af09e995e96ae173d1daa5c7)
1 #include <u.h>
2 #include <libc.h>
3 
4 #define	TZSIZE	((136*2)+10)		/* 1970-2106; match tm2sec.c */
5 
6 static	void	readtimezone(void);
7 static	int	rd_name(char**, char*);
8 static	int	rd_long(char**, long*);
9 
10 static
11 struct
12 {
13 	char	stname[4];
14 	char	dlname[4];
15 	long	stdiff;
16 	long	dldiff;
17 	ulong	dlpairs[TZSIZE];
18 } timezone;
19 
20 #define SEC2MIN 60UL
21 #define SEC2HOUR (60UL*SEC2MIN)
22 #define SEC2DAY (24UL*SEC2HOUR)
23 
24 /*
25  *  days per month plus days/year
26  */
27 static	int	dmsize[] =
28 {
29 	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
30 };
31 static	int	ldmsize[] =
32 {
33 	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
34 };
35 
36 /*
37  *  return the days/month for the given year
38  */
39 static int *
yrsize(int y)40 yrsize(int y)
41 {
42 	if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
43 		return ldmsize;
44 	else
45 		return dmsize;
46 }
47 
48 /*
49  * compute seconds since Jan 1 1970 GMT
50  * and convert to our timezone.
51  */
52 long
tm2sec(Tm * tm)53 tm2sec(Tm *tm)
54 {
55 	ulong secs;
56 	int i, yday, year, *d2m;
57 
58 	if(strcmp(tm->zone, "GMT") != 0 && timezone.stname[0] == 0)
59 		readtimezone();
60 	secs = 0;
61 
62 	/*
63 	 *  seconds per year
64 	 */
65 	year = tm->year + 1900;
66 	for(i = 1970; i < year; i++){
67 		d2m = yrsize(i);
68 		secs += d2m[0] * SEC2DAY;
69 	}
70 
71 	/*
72 	 *  if mday is set, use mon and mday to compute yday
73 	 */
74 	if(tm->mday){
75 		yday = 0;
76 		d2m = yrsize(year);
77 		for(i=0; i<tm->mon; i++)
78 			yday += d2m[i+1];
79 		yday += tm->mday-1;
80 	}else{
81 		yday = tm->yday;
82 	}
83 	secs += yday * SEC2DAY;
84 
85 	/*
86 	 * hours, minutes, seconds
87 	 */
88 	secs += tm->hour * SEC2HOUR;
89 	secs += tm->min * SEC2MIN;
90 	secs += tm->sec;
91 
92 	/*
93 	 * Only handles zones mentioned in /env/timezone,
94 	 * but things get too ambiguous otherwise.
95 	 */
96 	if(strcmp(tm->zone, timezone.stname) == 0)
97 		secs -= timezone.stdiff;
98 	else if(strcmp(tm->zone, timezone.dlname) == 0)
99 		secs -= timezone.dldiff;
100 	return secs;
101 }
102 
103 static
104 void
readtimezone(void)105 readtimezone(void)
106 {
107 	char buf[TZSIZE*11+30], *p;
108 	int i;
109 
110 	memset(buf, 0, sizeof(buf));
111 	i = open("/env/timezone", 0);
112 	if(i < 0)
113 		goto error;
114 	if(read(i, buf, sizeof(buf)) >= sizeof(buf))
115 		goto error;
116 	close(i);
117 	p = buf;
118 	if(rd_name(&p, timezone.stname))
119 		goto error;
120 	if(rd_long(&p, &timezone.stdiff))
121 		goto error;
122 	if(rd_name(&p, timezone.dlname))
123 		goto error;
124 	if(rd_long(&p, &timezone.dldiff))
125 		goto error;
126 	for(i=0; i<TZSIZE; i++) {
127 		if(rd_long(&p, (long *)&timezone.dlpairs[i]))
128 			goto error;
129 		if(timezone.dlpairs[i] == 0)
130 			return;
131 	}
132 	/* array too small for input */
133 error:
134 	timezone.stdiff = 0;
135 	strcpy(timezone.stname, "GMT");
136 	timezone.dlpairs[0] = 0;
137 }
138 
139 static int
rd_name(char ** f,char * p)140 rd_name(char **f, char *p)
141 {
142 	int c, i;
143 
144 	for(;;) {
145 		c = *(*f)++;
146 		if(c != ' ' && c != '\n')
147 			break;
148 	}
149 	for(i=0; i<3; i++) {
150 		if(c == ' ' || c == '\n')
151 			return 1;
152 		*p++ = c;
153 		c = *(*f)++;
154 	}
155 	if(c != ' ' && c != '\n')
156 		return 1;
157 	*p = 0;
158 	return 0;
159 }
160 
161 static int
rd_long(char ** f,long * p)162 rd_long(char **f, long *p)
163 {
164 	int c, s;
165 	long l;
166 
167 	s = 0;
168 	for(;;) {
169 		c = *(*f)++;
170 		if(c == '-') {
171 			s++;
172 			continue;
173 		}
174 		if(c != ' ' && c != '\n')
175 			break;
176 	}
177 	if(c == 0) {
178 		*p = 0;
179 		return 0;
180 	}
181 	l = 0;
182 	for(;;) {
183 		if(c == ' ' || c == '\n')
184 			break;
185 		if(c < '0' || c > '9')
186 			return 1;
187 		l = l*10 + c-'0';
188 		c = *(*f)++;
189 	}
190 	if(s)
191 		l = -l;
192 	*p = l;
193 	return 0;
194 }
195