ngx_parse_time.c revision e18a033b
1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10
11
12static ngx_uint_t  mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
13
14time_t
15ngx_parse_http_time(u_char *value, size_t len)
16{
17    u_char      *p, *end;
18    ngx_int_t    month;
19    ngx_uint_t   day, year, hour, min, sec;
20    uint64_t     time;
21    enum {
22        no = 0,
23        rfc822,   /* Tue, 10 Nov 2002 23:50:13   */
24        rfc850,   /* Tuesday, 10-Dec-02 23:50:13 */
25        isoc      /* Tue Dec 10 23:50:13 2002    */
26    } fmt;
27
28    fmt = 0;
29    end = value + len;
30
31#if (NGX_SUPPRESS_WARN)
32    day = 32;
33    year = 2038;
34#endif
35
36    for (p = value; p < end; p++) {
37        if (*p == ',') {
38            break;
39        }
40
41        if (*p == ' ') {
42            fmt = isoc;
43            break;
44        }
45    }
46
47    for (p++; p < end; p++)
48        if (*p != ' ') {
49            break;
50        }
51
52    if (end - p < 18) {
53        return NGX_ERROR;
54        }
55
56    if (fmt != isoc) {
57        if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
58            return NGX_ERROR;
59        }
60
61        day = (*p - '0') * 10 + *(p + 1) - '0';
62        p += 2;
63
64        if (*p == ' ') {
65            if (end - p < 18) {
66                return NGX_ERROR;
67            }
68            fmt = rfc822;
69
70        } else if (*p == '-') {
71            fmt = rfc850;
72
73        } else {
74            return NGX_ERROR;
75        }
76
77        p++;
78    }
79
80    switch (*p) {
81
82    case 'J':
83        month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6;
84        break;
85
86    case 'F':
87        month = 1;
88        break;
89
90    case 'M':
91        month = *(p + 2) == 'r' ? 2 : 4;
92        break;
93
94    case 'A':
95        month = *(p + 1) == 'p' ? 3 : 7;
96        break;
97
98    case 'S':
99        month = 8;
100        break;
101
102    case 'O':
103        month = 9;
104        break;
105
106    case 'N':
107        month = 10;
108        break;
109
110    case 'D':
111        month = 11;
112        break;
113
114    default:
115        return NGX_ERROR;
116    }
117
118    p += 3;
119
120    if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) {
121        return NGX_ERROR;
122    }
123
124    p++;
125
126    if (fmt == rfc822) {
127        if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
128            || *(p + 2) < '0' || *(p + 2) > '9'
129            || *(p + 3) < '0' || *(p + 3) > '9')
130        {
131            return NGX_ERROR;
132        }
133
134        year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
135               + (*(p + 2) - '0') * 10 + *(p + 3) - '0';
136        p += 4;
137
138    } else if (fmt == rfc850) {
139        if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
140            return NGX_ERROR;
141        }
142
143        year = (*p - '0') * 10 + *(p + 1) - '0';
144        year += (year < 70) ? 2000 : 1900;
145        p += 2;
146    }
147
148    if (fmt == isoc) {
149        if (*p == ' ') {
150            p++;
151        }
152
153        if (*p < '0' || *p > '9') {
154            return NGX_ERROR;
155        }
156
157        day = *p++ - '0';
158
159        if (*p != ' ') {
160            if (*p < '0' || *p > '9') {
161                return NGX_ERROR;
162            }
163
164            day = day * 10 + *p++ - '0';
165        }
166
167        if (end - p < 14) {
168            return NGX_ERROR;
169        }
170    }
171
172    if (*p++ != ' ') {
173        return NGX_ERROR;
174    }
175
176    if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
177        return NGX_ERROR;
178    }
179
180    hour = (*p - '0') * 10 + *(p + 1) - '0';
181    p += 2;
182
183    if (*p++ != ':') {
184        return NGX_ERROR;
185    }
186
187    if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
188        return NGX_ERROR;
189    }
190
191    min = (*p - '0') * 10 + *(p + 1) - '0';
192    p += 2;
193
194    if (*p++ != ':') {
195        return NGX_ERROR;
196    }
197
198    if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
199        return NGX_ERROR;
200    }
201
202    sec = (*p - '0') * 10 + *(p + 1) - '0';
203
204    if (fmt == isoc) {
205        p += 2;
206
207        if (*p++ != ' ') {
208            return NGX_ERROR;
209        }
210
211        if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
212            || *(p + 2) < '0' || *(p + 2) > '9'
213            || *(p + 3) < '0' || *(p + 3) > '9')
214        {
215            return NGX_ERROR;
216        }
217
218        year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
219               + (*(p + 2) - '0') * 10 + *(p + 3) - '0';
220    }
221
222    if (hour > 23 || min > 59 || sec > 59) {
223        return NGX_ERROR;
224    }
225
226    if (day == 29 && month == 1) {
227        if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) {
228            return NGX_ERROR;
229        }
230
231    } else if (day > mday[month]) {
232        return NGX_ERROR;
233    }
234
235    /*
236     * shift new year to March 1 and start months from 1 (not 0),
237     * it is needed for Gauss' formula
238     */
239
240    if (--month <= 0) {
241        month += 12;
242        year -= 1;
243    }
244
245    /* Gauss' formula for Gregorian days since March 1, 1 BC */
246
247    time = (uint64_t) (
248            /* days in years including leap years since March 1, 1 BC */
249
250            365 * year + year / 4 - year / 100 + year / 400
251
252            /* days before the month */
253
254            + 367 * month / 12 - 30
255
256            /* days before the day */
257
258            + day - 1
259
260            /*
261             * 719527 days were between March 1, 1 BC and March 1, 1970,
262             * 31 and 28 days were in January and February 1970
263             */
264
265            - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec;
266
267#if (NGX_TIME_T_SIZE <= 4)
268
269    if (time > 0x7fffffff) {
270        return NGX_ERROR;
271    }
272
273#endif
274
275    return (time_t) time;
276}
277