summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_time_parse.c
diff options
context:
space:
mode:
authorIgor Sysoev <igor@sysoev.ru>2017-01-17 20:00:00 +0300
committerIgor Sysoev <igor@sysoev.ru>2017-01-17 20:00:00 +0300
commit16cbf3c076a0aca6d47adaf3f719493674cf2363 (patch)
treee6530480020f62a2bdbf249988ec3e2a751d3927 /src/nxt_time_parse.c
downloadunit-16cbf3c076a0aca6d47adaf3f719493674cf2363.tar.gz
unit-16cbf3c076a0aca6d47adaf3f719493674cf2363.tar.bz2
Initial version.
Diffstat (limited to '')
-rw-r--r--src/nxt_time_parse.c489
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;
+}