xref: /plan9-contrib/sys/src/cmd/ip/imap4d/date.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <auth.h>
5 #include "imap4d.h"
6 
7 char *
8 wdayname[7] =
9 {
10 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
11 };
12 
13 char *
14 monname[12] =
15 {
16 	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
17 };
18 
19 static void	time2tm(Tm *tm, char *s);
20 static void	zone2tm(Tm *tm, char *s);
21 static int	dateindex(char *d, char **tab, int n);
22 
23 int
24 rfc822date(char *s, int n, Tm *tm)
25 {
26 	char *plus;
27 	int m;
28 
29 	plus = "+";
30 	if(tm->tzoff < 0)
31 		plus = "";
32 	m = 0;
33 	if(tm->wday != 8){
34 		m = snprint(s, n, "%s, ", wdayname[tm->wday]);
35 		if(m < 0)
36 			return m;
37 	}
38 	return snprint(s+m, n-m, "%.2d %s %.4d %.2d:%.2d:%.2d %s%.4d",
39 		tm->mday, monname[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec,
40 		plus, (tm->tzoff/3600)*100 + (tm->tzoff/60)%60);
41 }
42 
43 int
44 imap4date(char *s, int n, Tm *tm)
45 {
46 	char *plus;
47 
48 	plus = "+";
49 	if(tm->tzoff < 0)
50 		plus = "";
51 	return snprint(s, n, "%2d-%s-%.4d %2.2d:%2.2d:%2.2d %s%4.4d",
52 		tm->mday, monname[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec, plus, (tm->tzoff/3600)*100 + (tm->tzoff/60)%60);
53 }
54 
55 int
56 imap4Date(Tm *tm, char *date)
57 {
58 	char *flds[4];
59 
60 	if(getfields(date, flds, 3, 0, "-") != 3)
61 		return 0;
62 
63 	tm->mday = strtol(flds[0], nil, 10);
64 	tm->mon = dateindex(flds[1], monname, 12);
65 	tm->year = strtol(flds[2], nil, 10) - 1900;
66 	return 1;
67 }
68 
69 /*
70  * parse imap4 dates
71  */
72 ulong
73 imap4DateTime(char *date)
74 {
75 	Tm tm;
76 	char *flds[4], *sflds[4];
77 	ulong t;
78 
79 	if(getfields(date, flds, 4, 0, " ") != 3)
80 		return ~0;
81 
82 	if(!imap4Date(&tm, flds[0]))
83 		return ~0;
84 
85 	if(getfields(flds[1], sflds, 3, 0, ":") != 3)
86 		return ~0;
87 
88 	tm.hour = strtol(sflds[0], nil, 10);
89 	tm.min = strtol(sflds[1], nil, 10);
90 	tm.sec = strtol(sflds[2], nil, 10);
91 
92 	strcpy(tm.zone, "GMT");
93 	t = tm2sec(&tm);
94 	zone2tm(&tm, flds[2]);
95 	t -= tm.tzoff;
96 	return t;
97 }
98 
99 /*
100  * parse dates of formats
101  * [Wkd[,]] DD Mon YYYY HH:MM:SS zone
102  * [Wkd] Mon ( D|DD) HH:MM:SS zone YYYY
103  * plus anything similar
104  * return nil for a failure
105  */
106 Tm*
107 date2tm(Tm *tm, char *date)
108 {
109 	Tm gmt, *atm;
110 	char *flds[7], *s, dstr[64];
111 	int n;
112 
113 	/*
114 	 * default date is Thu Jan  1 00:00:00 GMT 1970
115 	 */
116 	tm->wday = 4;
117 	tm->mday = 1;
118 	tm->mon = 1;
119 	tm->hour = 0;
120 	tm->min = 0;
121 	tm->sec = 0;
122 	tm->year = 70;
123 	strcpy(tm->zone, "GMT");
124 	tm->tzoff = 0;
125 
126 	strncpy(dstr, date, sizeof(dstr));
127 	dstr[sizeof(dstr)-1] = '\0';
128 	n = tokenize(dstr, flds, 7);
129 	if(n != 6 && n != 5)
130 		return nil;
131 
132 	if(n == 5){
133 		for(n = 5; n >= 0; n--)
134 			flds[n] = flds[n - 1];
135 		n = 5;
136 	}else{
137 		/*
138 		 * Wday[,]
139 		 */
140 		s = strchr(flds[0], ',');
141 		if(s != nil)
142 			*s = '\0';
143 		tm->wday = dateindex(flds[0], wdayname, 7);
144 		if(tm->wday < 0)
145 			return nil;
146 	}
147 
148 	/*
149 	 * check for the two major formats:
150 	 * Month first or day first
151 	 */
152 	tm->mon = dateindex(flds[1], monname, 12);
153 	if(tm->mon >= 0){
154 		tm->mday = strtoul(flds[2], nil, 10);
155 		time2tm(tm, flds[3]);
156 		zone2tm(tm, flds[4]);
157 		tm->year = strtoul(flds[5], nil, 10);
158 		if(strlen(flds[5]) > 2)
159 			tm->year -= 1900;
160 	}else{
161 		tm->mday = strtoul(flds[1], nil, 10);
162 		tm->mon = dateindex(flds[2], monname, 12);
163 		tm->year = strtoul(flds[3], nil, 10);
164 		if(strlen(flds[3]) > 2)
165 			tm->year -= 1900;
166 		time2tm(tm, flds[4]);
167 		zone2tm(tm, flds[5]);
168 	}
169 
170 	if(n == 5){
171 		gmt = *tm;
172 		strncpy(gmt.zone, "", 4);
173 		gmt.tzoff = 0;
174 		atm = gmtime(tm2sec(&gmt));
175 		tm->wday = atm->wday;
176 	}else{
177 		/*
178 		 * Wday[,]
179 		 */
180 		s = strchr(flds[0], ',');
181 		if(s != nil)
182 			*s = '\0';
183 		tm->wday = dateindex(flds[0], wdayname, 7);
184 		if(tm->wday < 0)
185 			return nil;
186 	}
187 	return tm;
188 }
189 
190 /*
191  * zone	: [A-Za-z][A-Za-z][A-Za-z]	some time zone names
192  *	| [A-IK-Z]			military time; rfc1123 says the rfc822 spec is wrong.
193  *	| "UT"				universal time
194  *	| [+-][0-9][0-9][0-9][0-9]
195  * zones is the rfc-822 list of time zone names
196  */
197 static NamedInt zones[] =
198 {
199 	{"A",	-1 * 3600},
200 	{"B",	-2 * 3600},
201 	{"C",	-3 * 3600},
202 	{"CDT", -5 * 3600},
203 	{"CST", -6 * 3600},
204 	{"D",	-4 * 3600},
205 	{"E",	-5 * 3600},
206 	{"EDT", -4 * 3600},
207 	{"EST", -5 * 3600},
208 	{"F",	-6 * 3600},
209 	{"G",	-7 * 3600},
210 	{"GMT", 0},
211 	{"H",	-8 * 3600},
212 	{"I",	-9 * 3600},
213 	{"K",	-10 * 3600},
214 	{"L",	-11 * 3600},
215 	{"M",	-12 * 3600},
216 	{"MDT", -6 * 3600},
217 	{"MST", -7 * 3600},
218 	{"N",	+1 * 3600},
219 	{"O",	+2 * 3600},
220 	{"P",	+3 * 3600},
221 	{"PDT", -7 * 3600},
222 	{"PST", -8 * 3600},
223 	{"Q",	+4 * 3600},
224 	{"R",	+5 * 3600},
225 	{"S",	+6 * 3600},
226 	{"T",	+7 * 3600},
227 	{"U",	+8 * 3600},
228 	{"UT",	0},
229 	{"V",	+9 * 3600},
230 	{"W",	+10 * 3600},
231 	{"X",	+11 * 3600},
232 	{"Y",	+12 * 3600},
233 	{"Z",	0},
234 	{nil,	0}
235 };
236 
237 static void
238 zone2tm(Tm *tm, char *s)
239 {
240 	Tm aux, *atm;
241 	int i;
242 
243 	if(*s == '+' || *s == '-'){
244 		i = strtol(s, &s, 10);
245 		tm->tzoff = (i / 100) * 3600 + i % 100;
246 		strncpy(tm->zone, "", 4);
247 		return;
248 	}
249 
250 	/*
251 	 * look it up in the standard rfc822 table
252 	 */
253 	strncpy(tm->zone, s, 3);
254 	tm->zone[3] = '\0';
255 	tm->tzoff = 0;
256 	for(i = 0; zones[i].name != nil; i++){
257 		if(cistrcmp(zones[i].name, s) == 0){
258 			tm->tzoff = zones[i].v;
259 			return;
260 		}
261 	}
262 
263 	/*
264 	 * one last try: look it up in the current local timezone
265 	 * probe a couple of times to get daylight/standard time change.
266 	 */
267 	aux = *tm;
268 	memset(aux.zone, 0, 4);
269 	aux.hour--;
270 	for(i = 0; i < 2; i++){
271 		atm = localtime(tm2sec(&aux));
272 		if(cistrcmp(tm->zone, atm->zone) == 0){
273 			tm->tzoff = atm->tzoff;
274 			return;
275 		}
276 		aux.hour++;
277 	}
278 
279 	strncpy(tm->zone, "GMT", 4);
280 	tm->tzoff = 0;
281 }
282 
283 /*
284  * hh[:mm[:ss]]
285  */
286 static void
287 time2tm(Tm *tm, char *s)
288 {
289 	tm->hour = strtoul(s, &s, 10);
290 	if(*s++ != ':')
291 		return;
292 	tm->min = strtoul(s, &s, 10);
293 	if(*s++ != ':')
294 		return;
295 	tm->sec = strtoul(s, &s, 10);
296 }
297 
298 static int
299 dateindex(char *d, char **tab, int n)
300 {
301 	int i;
302 
303 	for(i = 0; i < n; i++)
304 		if(cistrcmp(d, tab[i]) == 0)
305 			return i;
306 	return -1;
307 }
308