diff options
author | Igor Sysoev <igor@sysoev.ru> | 2017-01-17 20:00:00 +0300 |
---|---|---|
committer | Igor Sysoev <igor@sysoev.ru> | 2017-01-17 20:00:00 +0300 |
commit | 16cbf3c076a0aca6d47adaf3f719493674cf2363 (patch) | |
tree | e6530480020f62a2bdbf249988ec3e2a751d3927 /src/nxt_time_parse.c | |
download | unit-16cbf3c076a0aca6d47adaf3f719493674cf2363.tar.gz unit-16cbf3c076a0aca6d47adaf3f719493674cf2363.tar.bz2 |
Initial version.
Diffstat (limited to '')
-rw-r--r-- | src/nxt_time_parse.c | 489 |
1 files changed, 489 insertions, 0 deletions
diff --git a/src/nxt_time_parse.c b/src/nxt_time_parse.c new file mode 100644 index 00000000..f79c78ab --- /dev/null +++ b/src/nxt_time_parse.c @@ -0,0 +1,489 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> + + +/* + * nxt_time_parse() parses a time string given in RFC822, RFC850, or ISOC + * formats and returns nxt_time_t value >= 0 on success or -1 on failure. + */ + +nxt_time_t +nxt_time_parse(const u_char *p, size_t len) +{ + size_t n; + u_char c; + uint64_t s; + nxt_int_t yr, month, day, hour, min, sec; + nxt_uint_t year, days; + const u_char *end; + + static nxt_int_t mday[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + + enum { + RFC822 = 0, /* "Mon, 28 Sep 1970 12:00:00" */ + RFC850, /* "Monday, 28-Sep-70 12:00:00" */ + ISOC, /* "Mon Sep 28 12:00:00 1970" */ + } fmt; + + fmt = RFC822; + end = p + len; + + while (p < end) { + c = *p++; + + if (c == ',') { + break; + } + + if (c == ' ') { + fmt = ISOC; + break; + } + } + + while (p < end) { + if (*p != ' ') { + break; + } + + p++; + } + + if (nxt_slow_path(p + 18 > end)) { + /* Lesser than RFC850 "28-Sep-70 12:00:00" length. */ + return -1; + } + + day = 0; + + if (fmt != ISOC) { + day = nxt_int_parse(p, 2); + if (nxt_slow_path(day <= 0)) { + return -1; + } + p += 2; + + if (*p == ' ') { + if (nxt_slow_path(p + 18 > end)) { + /* Lesser than RFC822 " Sep 1970 12:00:00" length. */ + return -1; + } + + /* RFC822 */ + + } else if (*p == '-') { + fmt = RFC850; + + } else { + return -1; + } + + p++; + } + + switch (*p) { + + case 'J': + month = p[1] == 'a' ? 0 : p[2] == 'n' ? 5 : 6; + break; + + case 'F': + month = 1; + break; + + case 'M': + month = p[2] == 'r' ? 2 : 4; + break; + + case 'A': + month = p[1] == 'p' ? 3 : 7; + break; + + case 'S': + month = 8; + break; + + case 'O': + month = 9; + break; + + case 'N': + month = 10; + break; + + case 'D': + month = 11; + break; + + default: + return -1; + } + + p += 3; + yr = 0; + + switch (fmt) { + + case RFC822: + if (nxt_slow_path(*p++ != ' ')) { + return -1; + } + + yr = nxt_int_parse(p, 4); + if (nxt_slow_path(yr <= 0)) { + return -1; + } + p += 4; + + break; + + case RFC850: + if (nxt_slow_path(*p++ != '-')) { + return -1; + } + + yr = nxt_int_parse(p, 2); + if (nxt_slow_path(yr <= 0)) { + return -1; + } + p += 2; + + yr += (yr < 70) ? 2000 : 1900; + + break; + + default: /* ISOC */ + if (nxt_slow_path(*p++ != ' ')) { + return -1; + } + + if (p[0] != ' ') { + n = 2; + + if (p[1] == ' ') { + n = 1; + } + + } else { + p++; + n = 1; + } + + day = nxt_int_parse(p, n); + if (nxt_slow_path(day <= 0)) { + return -1; + } + p += n; + + if (nxt_slow_path(p + 14 > end)) { + /* Lesser than ISOC " 12:00:00 1970" length. */ + return -1; + } + + break; + } + + if (nxt_slow_path(*p++ != ' ')) { + return -1; + } + + hour = nxt_int_parse(p, 2); + if (nxt_slow_path(hour < 0)) { + return -1; + } + p += 2; + + if (nxt_slow_path(*p++ != ':')) { + return -1; + } + + min = nxt_int_parse(p, 2); + if (nxt_slow_path(min < 0)) { + return -1; + } + p += 2; + + if (nxt_slow_path(*p++ != ':')) { + return -1; + } + + sec = nxt_int_parse(p, 2); + if (nxt_slow_path(sec < 0)) { + return -1; + } + + if (fmt == ISOC) { + p += 2; + + if (nxt_slow_path(*p++ != ' ')) { + return -1; + } + + yr = nxt_int_parse(p, 4); + if (nxt_slow_path(yr < 0)) { + return -1; + } + } + + if (nxt_slow_path(hour > 23 || min > 59 || sec > 59)) { + return -1; + } + + year = yr; + + if (day == 29 && month == 1) { + + if (nxt_slow_path((year & 3) != 0)) { + /* Not a leap year. */ + return -1; + } + + if (nxt_slow_path((year % 100 == 0) && (year % 400) != 0)) { + /* Not a leap year. */ + return -1; + } + + } else if (nxt_slow_path(day > mday[(nxt_uint_t) month])) { + return -1; + } + + /* + * Shift new year to March 1 and start months + * from 1 (not 0), as required for Gauss' formula. + */ + + if (--month <= 0) { + month += 12; + year -= 1; + } + + /* Gauss' formula for Gregorian days since March 1, 1 BCE. */ + + /* Days in years including leap years since March 1, 1 BCE. */ + days = 365 * year + year / 4 - year / 100 + year / 400 + + /* Days before the month. */ + + 367 * (nxt_uint_t) month / 12 - 30 + + /* Days before the day. */ + + (nxt_uint_t) day - 1; + + /* + * 719527 days were between March 1, 1 BCE and March 1, 1970, + * 31 and 28 days were in January and February 1970. + */ + days = days - 719527 + 31 + 28; + + s = (uint64_t) days * 86400 + + (nxt_uint_t) hour * 3600 + + (nxt_uint_t) min * 60 + + (nxt_uint_t) sec; + +#if (NXT_TIME_T_SIZE <= 4) + + /* Y2038 */ + + if (nxt_slow_path(s > 0x7fffffff)) { + return -1; + } + +#endif + + return (nxt_time_t) s; +} + + +/* + * nxt_term_parse() parses term string given in format "200", "10m", + * or "1d 1h" and returns nxt_int_t value >= 0 on success, -1 on failure, + * and -2 on overflow. The maximum valid value is 2^31 - 1 or about + * 68 years in seconds or about 24 days in milliseconds. + */ + +nxt_int_t +nxt_term_parse(const u_char *p, size_t len, nxt_bool_t seconds) +{ + u_char c, ch; + nxt_uint_t val, term, scale, max; + const u_char *end; + + enum { + st_first_digit = 0, + st_digit, + st_letter, + st_space, + } state; + + enum { + st_start = 0, + st_year, + st_month, + st_week, + st_day, + st_hour, + st_min, + st_sec, + st_msec, + st_last, + } step; + + val = 0; + term = 0; + state = st_first_digit; + step = seconds ? st_start : st_month; + + end = p + len; + + while (p < end) { + + ch = *p++; + + if (state == st_space) { + + if (ch == ' ') { + continue; + } + + state = st_first_digit; + } + + if (state != st_letter) { + + /* Values below '0' become >= 208. */ + c = ch - '0'; + + if (c <= 9) { + val = val * 10 + c; + state = st_digit; + continue; + } + + if (state == st_first_digit) { + return -1; + } + + state = st_letter; + } + + switch (ch) { + + case 'y': + if (step > st_start) { + return -1; + } + step = st_year; + max = NXT_INT32_T_MAX / (365 * 24 * 60 * 60); + scale = 365 * 24 * 60 * 60; + break; + + case 'M': + if (step >= st_month) { + return -1; + } + step = st_month; + max = NXT_INT32_T_MAX / (30 * 24 * 60 * 60); + scale = 30 * 24 * 60 * 60; + break; + + case 'w': + if (step >= st_week) { + return -1; + } + step = st_week; + max = NXT_INT32_T_MAX / (7 * 24 * 60 * 60); + scale = 7 * 24 * 60 * 60; + break; + + case 'd': + if (step >= st_day) { + return -1; + } + step = st_day; + max = NXT_INT32_T_MAX / (24 * 60 * 60); + scale = 24 * 60 * 60; + break; + + case 'h': + if (step >= st_hour) { + return -1; + } + step = st_hour; + max = NXT_INT32_T_MAX / (60 * 60); + scale = 60 * 60; + break; + + case 'm': + if (p < end && *p == 's') { + if (seconds || step >= st_msec) { + return -1; + } + p++; + step = st_msec; + max = NXT_INT32_T_MAX; + scale = 1; + break; + } + + if (step >= st_min) { + return -1; + } + step = st_min; + max = NXT_INT32_T_MAX / 60; + scale = 60; + break; + + case 's': + if (step >= st_sec) { + return -1; + } + step = st_sec; + max = NXT_INT32_T_MAX; + scale = 1; + break; + + case ' ': + if (step >= st_sec) { + return -1; + } + step = st_last; + max = NXT_INT32_T_MAX; + scale = 1; + break; + + default: + return -1; + } + + if (!seconds && step != st_msec) { + scale *= 1000; + max /= 1000; + } + + if (val > max) { + return -2; + } + + term += val * scale; + + if (term > NXT_INT32_T_MAX) { + return -2; + } + + val = 0; + + state = st_space; + } + + if (!seconds) { + val *= 1000; + } + + return term + val; +} |