xref: /plan9/sys/src/cmd/ip/imap4d/date.c (revision dba79dc8121478fd8794e9b3575bf7e9783bb69c)
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
rfc822date(char * s,int n,Tm * tm)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(0 <= tm->wday && tm->wday < 7){
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
imap4date(char * s,int n,Tm * tm)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
imap4Date(Tm * tm,char * date)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
imap4DateTime(char * date)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 	tm.yday = 0;
94 	t = tm2sec(&tm);
95 	zone2tm(&tm, flds[2]);
96 	t -= tm.tzoff;
97 	return t;
98 }
99 
100 /*
101  * parse dates of formats
102  * [Wkd[,]] DD Mon YYYY HH:MM:SS zone
103  * [Wkd] Mon ( D|DD) HH:MM:SS zone YYYY
104  * plus anything similar
105  * return nil for a failure
106  */
107 Tm*
date2tm(Tm * tm,char * date)108 date2tm(Tm *tm, char *date)
109 {
110 	Tm gmt, *atm;
111 	char *flds[7], *s, dstr[64];
112 	int n;
113 
114 	/*
115 	 * default date is Thu Jan  1 00:00:00 GMT 1970
116 	 */
117 	tm->wday = 4;
118 	tm->mday = 1;
119 	tm->mon = 1;
120 	tm->hour = 0;
121 	tm->min = 0;
122 	tm->sec = 0;
123 	tm->year = 70;
124 	strcpy(tm->zone, "GMT");
125 	tm->tzoff = 0;
126 
127 	strncpy(dstr, date, sizeof(dstr));
128 	dstr[sizeof(dstr)-1] = '\0';
129 	n = tokenize(dstr, flds, 7);
130 	if(n != 6 && n != 5)
131 		return nil;
132 
133 	if(n == 5){
134 		for(n = 5; n >= 1; n--)
135 			flds[n] = flds[n - 1];
136 		n = 5;
137 	}else{
138 		/*
139 		 * Wday[,]
140 		 */
141 		s = strchr(flds[0], ',');
142 		if(s != nil)
143 			*s = '\0';
144 		tm->wday = dateindex(flds[0], wdayname, 7);
145 		if(tm->wday < 0)
146 			return nil;
147 	}
148 
149 	/*
150 	 * check for the two major formats:
151 	 * Month first or day first
152 	 */
153 	tm->mon = dateindex(flds[1], monname, 12);
154 	if(tm->mon >= 0){
155 		tm->mday = strtoul(flds[2], nil, 10);
156 		time2tm(tm, flds[3]);
157 		zone2tm(tm, flds[4]);
158 		tm->year = strtoul(flds[5], nil, 10);
159 		if(strlen(flds[5]) > 2)
160 			tm->year -= 1900;
161 	}else{
162 		tm->mday = strtoul(flds[1], nil, 10);
163 		tm->mon = dateindex(flds[2], monname, 12);
164 		tm->year = strtoul(flds[3], nil, 10);
165 		if(strlen(flds[3]) > 2)
166 			tm->year -= 1900;
167 		time2tm(tm, flds[4]);
168 		zone2tm(tm, flds[5]);
169 	}
170 
171 	if(n == 5){
172 		gmt = *tm;
173 		strncpy(gmt.zone, "", 4);
174 		gmt.tzoff = 0;
175 		atm = gmtime(tm2sec(&gmt));
176 		tm->wday = atm->wday;
177 	}else{
178 		/*
179 		 * Wday[,]
180 		 */
181 		s = strchr(flds[0], ',');
182 		if(s != nil)
183 			*s = '\0';
184 		tm->wday = dateindex(flds[0], wdayname, 7);
185 		if(tm->wday < 0)
186 			return nil;
187 	}
188 	return tm;
189 }
190 
191 /*
192  * zone	: [A-Za-z][A-Za-z][A-Za-z]	some time zone names
193  *	| [A-IK-Z]			military time; rfc1123 says the rfc822 spec is wrong.
194  *	| "UT"				universal time
195  *	| [+-][0-9][0-9][0-9][0-9]
196  * zones is the rfc-822 list of time zone names
197  */
198 static NamedInt zones[] =
199 {
200 	{"A",	-1 * 3600},
201 	{"B",	-2 * 3600},
202 	{"C",	-3 * 3600},
203 	{"CDT", -5 * 3600},
204 	{"CST", -6 * 3600},
205 	{"D",	-4 * 3600},
206 	{"E",	-5 * 3600},
207 	{"EDT", -4 * 3600},
208 	{"EST", -5 * 3600},
209 	{"F",	-6 * 3600},
210 	{"G",	-7 * 3600},
211 	{"GMT", 0},
212 	{"H",	-8 * 3600},
213 	{"I",	-9 * 3600},
214 	{"K",	-10 * 3600},
215 	{"L",	-11 * 3600},
216 	{"M",	-12 * 3600},
217 	{"MDT", -6 * 3600},
218 	{"MST", -7 * 3600},
219 	{"N",	+1 * 3600},
220 	{"O",	+2 * 3600},
221 	{"P",	+3 * 3600},
222 	{"PDT", -7 * 3600},
223 	{"PST", -8 * 3600},
224 	{"Q",	+4 * 3600},
225 	{"R",	+5 * 3600},
226 	{"S",	+6 * 3600},
227 	{"T",	+7 * 3600},
228 	{"U",	+8 * 3600},
229 	{"UT",	0},
230 	{"V",	+9 * 3600},
231 	{"W",	+10 * 3600},
232 	{"X",	+11 * 3600},
233 	{"Y",	+12 * 3600},
234 	{"Z",	0},
235 	{nil,	0}
236 };
237 
238 static void
zone2tm(Tm * tm,char * s)239 zone2tm(Tm *tm, char *s)
240 {
241 	Tm aux, *atm;
242 	int i;
243 
244 	if(*s == '+' || *s == '-'){
245 		i = strtol(s, &s, 10);
246 		tm->tzoff = (i / 100) * 3600 + i % 100;
247 		strncpy(tm->zone, "", 4);
248 		return;
249 	}
250 
251 	/*
252 	 * look it up in the standard rfc822 table
253 	 */
254 	strncpy(tm->zone, s, 3);
255 	tm->zone[3] = '\0';
256 	tm->tzoff = 0;
257 	for(i = 0; zones[i].name != nil; i++){
258 		if(cistrcmp(zones[i].name, s) == 0){
259 			tm->tzoff = zones[i].v;
260 			return;
261 		}
262 	}
263 
264 	/*
265 	 * one last try: look it up in the current local timezone
266 	 * probe a couple of times to get daylight/standard time change.
267 	 */
268 	aux = *tm;
269 	memset(aux.zone, 0, 4);
270 	aux.hour--;
271 	for(i = 0; i < 2; i++){
272 		atm = localtime(tm2sec(&aux));
273 		if(cistrcmp(tm->zone, atm->zone) == 0){
274 			tm->tzoff = atm->tzoff;
275 			return;
276 		}
277 		aux.hour++;
278 	}
279 
280 	strncpy(tm->zone, "GMT", 4);
281 	tm->tzoff = 0;
282 }
283 
284 /*
285  * hh[:mm[:ss]]
286  */
287 static void
time2tm(Tm * tm,char * s)288 time2tm(Tm *tm, char *s)
289 {
290 	tm->hour = strtoul(s, &s, 10);
291 	if(*s++ != ':')
292 		return;
293 	tm->min = strtoul(s, &s, 10);
294 	if(*s++ != ':')
295 		return;
296 	tm->sec = strtoul(s, &s, 10);
297 }
298 
299 static int
dateindex(char * d,char ** tab,int n)300 dateindex(char *d, char **tab, int n)
301 {
302 	int i;
303 
304 	for(i = 0; i < n; i++)
305 		if(cistrcmp(d, tab[i]) == 0)
306 			return i;
307 	return -1;
308 }
309