xref: /unit/src/nxt_time_parse.c (revision 2078:0996dd223cdd)
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
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 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
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