1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7 #include <nxt_main.h>
8
9
10 /*
11 * nxt_time_parse() parses a time string given in RFC822, RFC850, or ISOC
12 * formats and returns nxt_time_t value >= 0 on success or -1 on failure.
13 */
14
15 nxt_time_t
nxt_time_parse(const u_char * p,size_t len)16 nxt_time_parse(const u_char *p, size_t len)
17 {
18 size_t n;
19 u_char c;
20 uint64_t s;
21 nxt_int_t yr, month, day, hour, min, sec;
22 nxt_uint_t year, days;
23 const u_char *end;
24
25 static const nxt_int_t mday[12] = {
26 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
27 };
28
29 enum {
30 RFC822 = 0, /* "Mon, 28 Sep 1970 12:00:00" */
31 RFC850, /* "Monday, 28-Sep-70 12:00:00" */
32 ISOC, /* "Mon Sep 28 12:00:00 1970" */
33 } fmt;
34
35 fmt = RFC822;
36 end = p + len;
37
38 while (p < end) {
39 c = *p++;
40
41 if (c == ',') {
42 break;
43 }
44
45 if (c == ' ') {
46 fmt = ISOC;
47 break;
48 }
49 }
50
51 while (p < end) {
52 if (*p != ' ') {
53 break;
54 }
55
56 p++;
57 }
58
59 if (nxt_slow_path(p + 18 > end)) {
60 /* Lesser than RFC850 "28-Sep-70 12:00:00" length. */
61 return -1;
62 }
63
64 day = 0;
65
66 if (fmt != ISOC) {
67 day = nxt_int_parse(p, 2);
68 if (nxt_slow_path(day <= 0)) {
69 return -1;
70 }
71 p += 2;
72
73 if (*p == ' ') {
74 if (nxt_slow_path(p + 18 > end)) {
75 /* Lesser than RFC822 " Sep 1970 12:00:00" length. */
76 return -1;
77 }
78
79 /* RFC822 */
80
81 } else if (*p == '-') {
82 fmt = RFC850;
83
84 } else {
85 return -1;
86 }
87
88 p++;
89 }
90
91 switch (*p) {
92
93 case 'J':
94 month = p[1] == 'a' ? 0 : p[2] == 'n' ? 5 : 6;
95 break;
96
97 case 'F':
98 month = 1;
99 break;
100
101 case 'M':
102 month = p[2] == 'r' ? 2 : 4;
103 break;
104
105 case 'A':
106 month = p[1] == 'p' ? 3 : 7;
107 break;
108
109 case 'S':
110 month = 8;
111 break;
112
113 case 'O':
114 month = 9;
115 break;
116
117 case 'N':
118 month = 10;
119 break;
120
121 case 'D':
122 month = 11;
123 break;
124
125 default:
126 return -1;
127 }
128
129 p += 3;
130 yr = 0;
131
132 switch (fmt) {
133
134 case RFC822:
135 if (nxt_slow_path(*p++ != ' ')) {
136 return -1;
137 }
138
139 yr = nxt_int_parse(p, 4);
140 if (nxt_slow_path(yr <= 0)) {
141 return -1;
142 }
143 p += 4;
144
145 break;
146
147 case RFC850:
148 if (nxt_slow_path(*p++ != '-')) {
149 return -1;
150 }
151
152 yr = nxt_int_parse(p, 2);
153 if (nxt_slow_path(yr <= 0)) {
154 return -1;
155 }
156 p += 2;
157
158 yr += (yr < 70) ? 2000 : 1900;
159
160 break;
161
162 default: /* ISOC */
163 if (nxt_slow_path(*p++ != ' ')) {
164 return -1;
165 }
166
167 if (p[0] != ' ') {
168 n = 2;
169
170 if (p[1] == ' ') {
171 n = 1;
172 }
173
174 } else {
175 p++;
176 n = 1;
177 }
178
179 day = nxt_int_parse(p, n);
180 if (nxt_slow_path(day <= 0)) {
181 return -1;
182 }
183 p += n;
184
185 if (nxt_slow_path(p + 14 > end)) {
186 /* Lesser than ISOC " 12:00:00 1970" length. */
187 return -1;
188 }
189
190 break;
191 }
192
193 if (nxt_slow_path(*p++ != ' ')) {
194 return -1;
195 }
196
197 hour = nxt_int_parse(p, 2);
198 if (nxt_slow_path(hour < 0)) {
199 return -1;
200 }
201 p += 2;
202
203 if (nxt_slow_path(*p++ != ':')) {
204 return -1;
205 }
206
207 min = nxt_int_parse(p, 2);
208 if (nxt_slow_path(min < 0)) {
209 return -1;
210 }
211 p += 2;
212
213 if (nxt_slow_path(*p++ != ':')) {
214 return -1;
215 }
216
217 sec = nxt_int_parse(p, 2);
218 if (nxt_slow_path(sec < 0)) {
219 return -1;
220 }
221
222 if (fmt == ISOC) {
223 p += 2;
224
225 if (nxt_slow_path(*p++ != ' ')) {
226 return -1;
227 }
228
229 yr = nxt_int_parse(p, 4);
230 if (nxt_slow_path(yr < 0)) {
231 return -1;
232 }
233 }
234
235 if (nxt_slow_path(hour > 23 || min > 59 || sec > 59)) {
236 return -1;
237 }
238
239 year = yr;
240
241 if (day == 29 && month == 1) {
242
243 if (nxt_slow_path((year & 3) != 0)) {
244 /* Not a leap year. */
245 return -1;
246 }
247
248 if (nxt_slow_path((year % 100 == 0) && (year % 400) != 0)) {
249 /* Not a leap year. */
250 return -1;
251 }
252
253 } else if (nxt_slow_path(day > mday[(nxt_uint_t) month])) {
254 return -1;
255 }
256
257 /*
258 * Shift new year to March 1 and start months
259 * from 1 (not 0), as required for Gauss' formula.
260 */
261
262 if (--month <= 0) {
263 month += 12;
264 year -= 1;
265 }
266
267 /* Gauss' formula for Gregorian days since March 1, 1 BCE. */
268
269 /* Days in years including leap years since March 1, 1 BCE. */
270 days = 365 * year + year / 4 - year / 100 + year / 400
271
272 /* Days before the month. */
273 + 367 * (nxt_uint_t) month / 12 - 30
274
275 /* Days before the day. */
276 + (nxt_uint_t) day - 1;
277
278 /*
279 * 719527 days were between March 1, 1 BCE and March 1, 1970,
280 * 31 and 28 days were in January and February 1970.
281 */
282 days = days - 719527 + 31 + 28;
283
284 s = (uint64_t) days * 86400
285 + (nxt_uint_t) hour * 3600
286 + (nxt_uint_t) min * 60
287 + (nxt_uint_t) sec;
288
289 #if (NXT_TIME_T_SIZE <= 4)
290
291 /* Y2038 */
292
293 if (nxt_slow_path(s > 0x7FFFFFFF)) {
294 return -1;
295 }
296
297 #endif
298
299 return (nxt_time_t) s;
300 }
301
302
303 /*
304 * nxt_term_parse() parses term string given in format "200", "10m",
305 * or "1d 1h" and returns nxt_int_t value >= 0 on success, -1 on failure,
306 * and -2 on overflow. The maximum valid value is 2^31 - 1 or about
307 * 68 years in seconds or about 24 days in milliseconds.
308 */
309
310 nxt_int_t
nxt_term_parse(const u_char * p,size_t len,nxt_bool_t seconds)311 nxt_term_parse(const u_char *p, size_t len, nxt_bool_t seconds)
312 {
313 u_char c, ch;
314 nxt_uint_t val, term, scale, max;
315 const u_char *end;
316
317 enum {
318 st_first_digit = 0,
319 st_digit,
320 st_letter,
321 st_space,
322 } state;
323
324 enum {
325 st_start = 0,
326 st_year,
327 st_month,
328 st_week,
329 st_day,
330 st_hour,
331 st_min,
332 st_sec,
333 st_msec,
334 st_last,
335 } step;
336
337 val = 0;
338 term = 0;
339 state = st_first_digit;
340 step = seconds ? st_start : st_month;
341
342 end = p + len;
343
344 while (p < end) {
345
346 ch = *p++;
347
348 if (state == st_space) {
349
350 if (ch == ' ') {
351 continue;
352 }
353
354 state = st_first_digit;
355 }
356
357 if (state != st_letter) {
358
359 /* Values below '0' become >= 208. */
360 c = ch - '0';
361
362 if (c <= 9) {
363 val = val * 10 + c;
364 state = st_digit;
365 continue;
366 }
367
368 if (state == st_first_digit) {
369 return -1;
370 }
371
372 state = st_letter;
373 }
374
375 switch (ch) {
376
377 case 'y':
378 if (step > st_start) {
379 return -1;
380 }
381 step = st_year;
382 max = NXT_INT32_T_MAX / (365 * 24 * 60 * 60);
383 scale = 365 * 24 * 60 * 60;
384 break;
385
386 case 'M':
387 if (step >= st_month) {
388 return -1;
389 }
390 step = st_month;
391 max = NXT_INT32_T_MAX / (30 * 24 * 60 * 60);
392 scale = 30 * 24 * 60 * 60;
393 break;
394
395 case 'w':
396 if (step >= st_week) {
397 return -1;
398 }
399 step = st_week;
400 max = NXT_INT32_T_MAX / (7 * 24 * 60 * 60);
401 scale = 7 * 24 * 60 * 60;
402 break;
403
404 case 'd':
405 if (step >= st_day) {
406 return -1;
407 }
408 step = st_day;
409 max = NXT_INT32_T_MAX / (24 * 60 * 60);
410 scale = 24 * 60 * 60;
411 break;
412
413 case 'h':
414 if (step >= st_hour) {
415 return -1;
416 }
417 step = st_hour;
418 max = NXT_INT32_T_MAX / (60 * 60);
419 scale = 60 * 60;
420 break;
421
422 case 'm':
423 if (p < end && *p == 's') {
424 if (seconds || step >= st_msec) {
425 return -1;
426 }
427 p++;
428 step = st_msec;
429 max = NXT_INT32_T_MAX;
430 scale = 1;
431 break;
432 }
433
434 if (step >= st_min) {
435 return -1;
436 }
437 step = st_min;
438 max = NXT_INT32_T_MAX / 60;
439 scale = 60;
440 break;
441
442 case 's':
443 if (step >= st_sec) {
444 return -1;
445 }
446 step = st_sec;
447 max = NXT_INT32_T_MAX;
448 scale = 1;
449 break;
450
451 case ' ':
452 if (step >= st_sec) {
453 return -1;
454 }
455 step = st_last;
456 max = NXT_INT32_T_MAX;
457 scale = 1;
458 break;
459
460 default:
461 return -1;
462 }
463
464 if (!seconds && step != st_msec) {
465 scale *= 1000;
466 max /= 1000;
467 }
468
469 if (val > max) {
470 return -2;
471 }
472
473 term += val * scale;
474
475 if (term > NXT_INT32_T_MAX) {
476 return -2;
477 }
478
479 val = 0;
480
481 state = st_space;
482 }
483
484 if (!seconds) {
485 val *= 1000;
486 }
487
488 return term + val;
489 }
490