]> git.kaiwu.me - njs.git/commitdiff
Date() function.
authorIgor Sysoev <igor@sysoev.ru>
Fri, 8 Apr 2016 15:19:43 +0000 (18:19 +0300)
committerIgor Sysoev <igor@sysoev.ru>
Fri, 8 Apr 2016 15:19:43 +0000 (18:19 +0300)
16 files changed:
Makefile
njs/njs_builtin.c
njs/njs_date.c [new file with mode: 0644]
njs/njs_date.h [new file with mode: 0644]
njs/njs_function.h
njs/njs_generator.c
njs/njs_lexer_keyword.c
njs/njs_object.c
njs/njs_object_hash.h
njs/njs_parser.c
njs/njs_parser.h
njs/njs_vm.c
njs/njs_vm.h
njs/test/njs_unit_test.c
nxt/auto/configure
nxt/auto/time [new file with mode: 0644]

index cf02682d3938657982d8f253243271d54fbfd76d..e12f9ed921667411c253915a1ffc724beda12a5c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,7 @@ $(NXT_BUILDDIR)/libnjs.a: \
        $(NXT_BUILDDIR)/njs_array.o \
        $(NXT_BUILDDIR)/njs_function.o \
        $(NXT_BUILDDIR)/njs_regexp.o \
+       $(NXT_BUILDDIR)/njs_date.o \
        $(NXT_BUILDDIR)/njs_math.o \
        $(NXT_BUILDDIR)/njs_extern.o \
        $(NXT_BUILDDIR)/njs_variable.o \
@@ -48,6 +49,7 @@ $(NXT_BUILDDIR)/libnjs.a: \
                $(NXT_BUILDDIR)/njs_array.o \
                $(NXT_BUILDDIR)/njs_function.o \
                $(NXT_BUILDDIR)/njs_regexp.o \
+               $(NXT_BUILDDIR)/njs_date.o \
                $(NXT_BUILDDIR)/njs_math.o \
                $(NXT_BUILDDIR)/njs_extern.o \
                $(NXT_BUILDDIR)/njs_variable.o \
@@ -224,6 +226,20 @@ $(NXT_BUILDDIR)/njs_regexp.o: \
                -I$(NXT_LIB) -Injs $(NXT_PCRE_CFLAGS) \
                njs/njs_regexp.c
 
+$(NXT_BUILDDIR)/njs_date.o: \
+       $(NXT_BUILDDIR)/libnxt.a \
+       njs/njscript.h \
+       njs/njs_vm.h \
+       njs/njs_string.h \
+       njs/njs_object.h \
+       njs/njs_function.h \
+       njs/njs_date.h \
+       njs/njs_date.c \
+
+       $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_date.o $(NXT_CFLAGS) \
+               -I$(NXT_LIB) -Injs $(NXT_PCRE_CFLAGS) \
+               njs/njs_date.c
+
 $(NXT_BUILDDIR)/njs_math.o: \
        $(NXT_BUILDDIR)/libnxt.a \
        njs/njscript.h \
index 453d74f54bf9eb4c2e5beb51749aa5b7e2f368b3..8344f9c20b3dd68c59f81ded8fbdef3f020e7b2d 100644 (file)
@@ -20,6 +20,7 @@
 #include <njs_array.h>
 #include <njs_function.h>
 #include <njs_regexp.h>
+#include <njs_date.h>
 #include <njs_math.h>
 #include <string.h>
 
@@ -46,6 +47,7 @@ njs_builtin_objects_create(njs_vm_t *vm)
         &njs_string_prototype_init,
         &njs_function_prototype_init,
         &njs_regexp_prototype_init,
+        &njs_date_prototype_init,
     };
 
     static const njs_object_init_t    *function_init[] = {
@@ -56,6 +58,7 @@ njs_builtin_objects_create(njs_vm_t *vm)
         &njs_string_constructor_init,
         &njs_function_constructor_init,
         &njs_regexp_constructor_init,
+        &njs_date_constructor_init,
 
         &njs_eval_function_init,
     };
@@ -70,6 +73,8 @@ njs_builtin_objects_create(njs_vm_t *vm)
         { njs_function_constructor, { 0 } },
         { njs_regexp_constructor,
           { NJS_SKIP_ARG, NJS_STRING_ARG, NJS_STRING_ARG } },
+        { njs_date_constructor,     { 0 } },
+
         { njs_eval_function,        { 0 } },
     };
 
@@ -178,6 +183,10 @@ njs_builtin_objects_create(njs_vm_t *vm)
  * RegExp.__proto__             -> Function_Prototype,
  * RegExp_Prototype.__proto__   -> Object_Prototype,
  *
+ * Date(),
+ * Date.__proto__               -> Function_Prototype,
+ * Date_Prototype.__proto__     -> Object_Prototype,
+ *
  * eval(),
  * eval.__proto__               -> Function_Prototype.
  */
diff --git a/njs/njs_date.c b/njs/njs_date.c
new file mode 100644 (file)
index 0000000..5e8e3f9
--- /dev/null
@@ -0,0 +1,2104 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_alignment.h>
+#include <nxt_stub.h>
+#include <nxt_djb_hash.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_random.h>
+#include <nxt_time.h>
+#include <nxt_malloc.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_number.h>
+#include <njs_string.h>
+#include <njs_object.h>
+#include <njs_object_hash.h>
+#include <njs_function.h>
+#include <njs_date.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+
+/*
+ * njs_timegm() is used because
+ *   Solaris lacks timegm(),
+ *   FreeBSD and MacOSX timegm() cannot handle years before 1900.
+ */
+
+#define NJS_ISO_DATE_TIME_LEN  sizeof("+001970-09-28T12:00:00.000Z")
+
+#define NJS_DATE_TIME_LEN                                                     \
+    sizeof("Mon Sep 28 1970 12:00:00 GMT+0600 (XXXXX)")
+
+
+static nxt_noinline double njs_date_string_parse(njs_value_t *date);
+static const u_char *njs_date_skip_week_day(const u_char *p, const u_char *end);
+static const u_char *njs_date_skip_spaces(const u_char *p, const u_char *end);
+static nxt_noinline nxt_int_t njs_date_month_parse(const u_char *p,
+    const u_char *end);
+static nxt_noinline const u_char *njs_date_time_parse(struct tm *tm,
+    const u_char *p, const u_char *end);
+static nxt_noinline nxt_int_t njs_date_gmtoff_parse(const u_char *start,
+    const u_char *end);
+static nxt_noinline const u_char *njs_date_number_parse(int *value,
+    const u_char *p, const u_char *end, size_t size);
+static int64_t njs_timegm(struct tm *tm);
+static nxt_noinline njs_ret_t njs_date_string(njs_vm_t *vm, const char *fmt,
+    double time);
+static nxt_noinline double njs_date_time(struct tm *tm, int64_t ms);
+static double njs_date_utc_time(struct tm *tm, double time);
+static njs_ret_t njs_date_prototype_to_json_continuation(njs_vm_t *vm,
+    njs_value_t *args, nxt_uint_t nargs, njs_index_t retval);
+
+
+static const njs_value_t  njs_string_invalid_date = njs_string("Invalid Date");
+
+
+static nxt_noinline uint64_t
+njs_gettime(void)
+{
+    struct timeval  tv;
+
+    gettimeofday(&tv, NULL);
+
+    return (uint64_t) tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+
+njs_ret_t
+njs_date_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double      num;
+    int64_t     time, values[8];
+    nxt_uint_t  i, n;
+    njs_date_t  *date;
+    struct tm   tm;
+
+    if (vm->frame->ctor) {
+
+        if (nargs == 0) {
+            time = njs_gettime();
+
+        } else if (nargs == 2 && njs_is_string(&args[1])) {
+            time = njs_date_string_parse(&args[1]);
+
+        } else {
+            memset(values, 0, 8 * sizeof(int64_t));
+            /* Month. */
+            values[2] = 1;
+
+            n = nxt_min(8, nargs);
+
+            for (i = 1; i < n; i++) {
+                if (!njs_is_numeric(&args[i])) {
+                    return NJS_TRAP_NUMBER_ARG;
+                }
+
+                num = args[i].data.u.number;
+
+                if (njs_is_nan(num)) {
+                    nargs = 0;
+                    break;
+                }
+
+                values[i] = num;
+            }
+
+            if (nargs > 2) {
+                /* Year. */
+                if (values[1] > 99) {
+                    values[1] -= 1900;
+                }
+
+                tm.tm_year = values[1];
+                tm.tm_mon = values[2];
+                tm.tm_mday = values[3];
+                tm.tm_hour = values[4];
+                tm.tm_min = values[5];
+                tm.tm_sec = values[6];
+                tm.tm_isdst = -1;
+
+                time = (int64_t) mktime(&tm) * 1000 + values[7];
+
+            } else {
+                time = values[1];
+            }
+        }
+
+        date = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_date_t));
+        if (nxt_slow_path(date == NULL)) {
+            return NXT_ERROR;
+        }
+
+        nxt_lvlhsh_init(&date->object.hash);
+        nxt_lvlhsh_init(&date->object.shared_hash);
+        date->object.shared = 0;
+        date->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_DATE];
+
+        date->time = time;
+
+        vm->retval.data.u.date = date;
+        vm->retval.type = NJS_DATE;
+        vm->retval.data.truth = 1;
+
+        return NXT_OK;
+    }
+
+    return njs_date_string(vm, "%a %b %d %Y %T GMT%z (%Z)", njs_gettime());
+}
+
+
+static njs_ret_t
+njs_date_utc(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double      num, time;
+    struct tm   tm;
+    nxt_uint_t  i, n;
+    int32_t     values[8];
+
+    time = NJS_NAN;
+
+    if (nargs > 2) {
+        memset(values, 0, 8 * sizeof(int32_t));
+
+        n = nxt_min(8, nargs);
+
+        for (i = 1; i < n; i++) {
+            if (!njs_is_numeric(&args[i])) {
+                return NJS_TRAP_NUMBER_ARG;
+            }
+
+            num = args[i].data.u.number;
+
+            if (njs_is_nan(num)) {
+                goto done;
+            }
+
+            values[i] = num;
+        }
+
+        /* Year. */
+        if (values[1] > 99) {
+            values[1] -= 1900;
+        }
+
+        tm.tm_year = values[1];
+        tm.tm_mon = values[2];
+        tm.tm_mday = values[3];
+        tm.tm_hour = values[4];
+        tm.tm_min = values[5];
+        tm.tm_sec = values[6];
+
+        time = njs_timegm(&tm) * 1000 + values[7];
+    }
+
+done:
+
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static int64_t
+njs_timegm(struct tm *tm)
+{
+    int32_t  year, month, days;
+
+    year = tm->tm_year + 1900;
+
+    /*
+     * Shift new year to March 1 and start months
+     * from 1 (not 0), as required for Gauss' formula.
+     */
+
+    month = tm->tm_mon - 1;
+
+    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. */
+    days += 367 * month / 12 - 30;
+
+    /* Days before the day. */
+    if (year >= 0) {
+        days += tm->tm_mday - 1;
+
+    } else {
+        /* 1 BCE was a leap year. */
+        days += tm->tm_mday - 2;
+    }
+
+    /*
+     * 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;
+
+    return (int64_t) days * 86400
+            + tm->tm_hour * 3600
+            + tm->tm_min * 60
+            + tm->tm_sec;
+}
+
+
+static njs_ret_t
+njs_date_now(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    njs_number_set(&vm->retval, njs_gettime());
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_parse(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  time;
+
+    if (nargs > 1) {
+        time = njs_date_string_parse(&args[1]);
+
+    } else {
+        time = NJS_NAN;
+    }
+
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static nxt_noinline double
+njs_date_string_parse(njs_value_t *date)
+{
+    int                ext, ms, gmtoff;
+    struct tm          tm;
+    nxt_bool_t         sign;
+    const u_char       *p, *next, *end;
+    njs_string_prop_t  string;
+
+    (void) njs_string_prop(&string, date);
+
+    p = string.start;
+    end = p + string.size;
+
+    if (nxt_slow_path(p + 10 >= end)) {
+        return NJS_NAN;
+    }
+
+    if (*p == '+') {
+        p++;
+        sign = 1;
+
+    } else if (*p == '-') {
+        p++;
+        sign = 1;
+
+    } else {
+        sign = 0;
+    }
+
+    next = njs_date_number_parse(&tm.tm_year, p, end, 4);
+
+    if (next != NULL) {
+        /* ISO-8601 format: "1970-09-28T06:00:00.000Z" */
+
+        if (*next != '-') {
+            /* Extended ISO-8601 format: "+001970-09-28T06:00:00.000Z" */
+
+            next = njs_date_number_parse(&ext, next, end, 2);
+            if (nxt_slow_path(next == NULL)) {
+                return NJS_NAN;
+            }
+
+            tm.tm_year = tm.tm_year * 100 + ext;
+
+            if (string.start[0] == '-') {
+                if (tm.tm_year == 0) {
+                    return NJS_NAN;
+                }
+
+                tm.tm_year = -tm.tm_year;
+            }
+
+            if (*next != '-') {
+                return NJS_NAN;
+            }
+        }
+
+        tm.tm_year -= 1900;
+
+        p = njs_date_number_parse(&tm.tm_mon, next + 1, end, 2);
+        if (nxt_slow_path(p == NULL)) {
+            return NJS_NAN;
+        }
+
+        tm.tm_mon--;
+
+        if (nxt_slow_path(p >= end || *p != '-')) {
+            return NJS_NAN;
+        }
+
+        p = njs_date_number_parse(&tm.tm_mday, p + 1, end, 2);
+        if (nxt_slow_path(p == NULL)) {
+            return NJS_NAN;
+        }
+
+        if (nxt_slow_path(p >= end || *p != 'T')) {
+            return NJS_NAN;
+        }
+
+        p = njs_date_time_parse(&tm, p + 1, end);
+        if (nxt_slow_path(p == NULL)) {
+            return NJS_NAN;
+        }
+
+        if (nxt_slow_path(p >= end || *p != '.')) {
+            return NJS_NAN;
+        }
+
+        p = njs_date_number_parse(&ms, p + 1, end, 3);
+        if (nxt_slow_path(p == NULL)) {
+            return NJS_NAN;
+        }
+
+        if (nxt_slow_path(p >= end || *p != 'Z')) {
+            return NJS_NAN;
+        }
+
+        return njs_timegm(&tm) * 1000 + ms;
+    }
+
+    if (sign) {
+        return NJS_NAN;
+    }
+
+    p = njs_date_skip_week_day(p, end);
+    if (nxt_slow_path(p == NULL)) {
+        return NJS_NAN;
+    }
+
+    p = njs_date_skip_spaces(p, end);
+    if (nxt_slow_path(p == NULL)) {
+        return NJS_NAN;
+    }
+
+    next = njs_date_number_parse(&tm.tm_mday, p, end, 2);
+
+    if (next != NULL) {
+        /*
+         * RFC 2822 format:
+         *   "Mon, 28 Sep 1970 06:00:00 GMT",
+         *   "Mon, 28 Sep 1970 06:00:00 UTC",
+         *   "Mon, 28 Sep 1970 12:00:00 +0600".
+         */
+        p = njs_date_skip_spaces(next, end);
+        if (nxt_slow_path(p == NULL)) {
+            return NJS_NAN;
+        }
+
+        tm.tm_mon = njs_date_month_parse(p, end);
+        if (nxt_slow_path(tm.tm_mon < 0)) {
+            return NJS_NAN;
+        }
+
+        p = njs_date_skip_spaces(p + 3, end);
+        if (nxt_slow_path(p == NULL)) {
+            return NJS_NAN;
+        }
+
+        p = njs_date_number_parse(&tm.tm_year, p, end, 4);
+        if (nxt_slow_path(p == NULL)) {
+            return NJS_NAN;
+        }
+
+        tm.tm_year -= 1900;
+
+        p = njs_date_skip_spaces(p, end);
+        if (nxt_slow_path(p == NULL)) {
+            return NJS_NAN;
+        }
+
+        p = njs_date_time_parse(&tm, p, end);
+        if (nxt_slow_path(p == NULL)) {
+            return NJS_NAN;
+        }
+
+        p = njs_date_skip_spaces(p, end);
+        if (nxt_slow_path(p == NULL)) {
+            return NJS_NAN;
+        }
+
+        if (p + 2 < end) {
+
+            if ((p[0] == 'G' && p[1] == 'M' && p[2] == 'T')
+                || (p[0] == 'U' && p[1] == 'T' && p[2] == 'C'))
+            {
+                gmtoff = 0;
+
+            } else {
+                gmtoff = njs_date_gmtoff_parse(p, end);
+                if (nxt_slow_path(gmtoff == -1)) {
+                    return NJS_NAN;
+                }
+            }
+
+            return (njs_timegm(&tm) - gmtoff * 60) * 1000;
+        }
+
+        return NJS_NAN;
+    }
+
+    /* Date.toString() format: "Mon Sep 28 1970 12:00:00 GMT+0600". */
+
+    tm.tm_mon = njs_date_month_parse(p, end);
+    if (nxt_slow_path(tm.tm_mon < 0)) {
+        return NJS_NAN;
+    }
+
+    p = njs_date_skip_spaces(p + 3, end);
+    if (nxt_slow_path(p == NULL)) {
+        return NJS_NAN;
+    }
+
+    p = njs_date_number_parse(&tm.tm_mday, p, end, 2);
+    if (nxt_slow_path(p == NULL)) {
+        return NJS_NAN;
+    }
+
+    p = njs_date_skip_spaces(p, end);
+    if (nxt_slow_path(p == NULL)) {
+        return NJS_NAN;
+    }
+
+    p = njs_date_number_parse(&tm.tm_year, p, end, 4);
+    if (nxt_slow_path(p == NULL)) {
+        return NJS_NAN;
+    }
+
+    tm.tm_year -= 1900;
+
+    p = njs_date_skip_spaces(p, end);
+    if (nxt_slow_path(p == NULL)) {
+        return NJS_NAN;
+    }
+
+    p = njs_date_time_parse(&tm, p, end);
+    if (nxt_slow_path(p == NULL)) {
+        return NJS_NAN;
+    }
+
+    p = njs_date_skip_spaces(p, end);
+    if (nxt_slow_path(p == NULL)) {
+        return NJS_NAN;
+    }
+
+    if (p + 2 < end && p[0] == 'G' && p[1] == 'M' && p[2] == 'T') {
+
+        gmtoff = njs_date_gmtoff_parse(&p[3], end);
+
+        if (nxt_fast_path(gmtoff != -1)) {
+            return (njs_timegm(&tm) - gmtoff * 60) * 1000;
+        }
+    }
+
+    return NJS_NAN;
+}
+
+
+static const u_char *
+njs_date_skip_week_day(const u_char *p, const u_char *end)
+{
+    while (p < end) {
+        if (*p == ' ') {
+            return p;
+        }
+
+        p++;
+    }
+
+    return NULL;
+}
+
+
+static const u_char *
+njs_date_skip_spaces(const u_char *p, const u_char *end)
+{
+    if (p < end && *p++ == ' ') {
+
+        while (p < end) {
+            if (*p != ' ') {
+                return p;
+            }
+
+            p++;
+        }
+    }
+
+    return NULL;
+}
+
+
+static nxt_noinline nxt_int_t
+njs_date_month_parse(const u_char *p, const u_char *end)
+{
+    if (p + 2 < end) {
+        switch (p[0]) {
+
+        case 'J':
+            if (p[1] == 'a' && p[2] == 'n') {
+                return 0;
+            }
+
+            if (p[1] == 'u') {
+                if (p[2] == 'n') {
+                    return 5;
+                }
+
+                if (p[2] == 'l') {
+                    return 6;
+                }
+            }
+
+            break;
+
+        case 'F':
+            if (p[1] == 'e' && p[2] == 'b') {
+                return 1;
+            }
+
+            break;
+
+        case 'M':
+            if (p[1] == 'a') {
+                if (p[2] == 'r') {
+                    return 2;
+                }
+
+                if (p[2] == 'y') {
+                    return 4;
+                }
+            }
+
+            break;
+
+        case 'A':
+            if (p[1] == 'p' && p[2] == 'r') {
+                return 3;
+            }
+
+            if (p[1] == 'u' && p[2] == 'g') {
+                return 7;
+            }
+
+            break;
+
+        case 'S':
+            if (p[1] == 'e' && p[2] == 'p') {
+                return 8;
+            }
+
+            break;
+
+        case 'O':
+            if (p[1] == 'c' && p[2] == 't') {
+                return 9;
+            }
+
+            break;
+
+        case 'N':
+            if (p[1] == 'o' && p[2] == 'v') {
+                return 10;
+            }
+
+            break;
+
+        case 'D':
+            if (p[1] == 'e' && p[2] == 'c') {
+                return 11;
+            }
+
+            break;
+        }
+    }
+
+    return -1;
+}
+
+
+static nxt_noinline const u_char *
+njs_date_time_parse(struct tm *tm, const u_char *p, const u_char *end)
+{
+    p = njs_date_number_parse(&tm->tm_hour, p, end, 2);
+    if (nxt_slow_path(p == NULL)) {
+        return p;
+    }
+
+    if (nxt_slow_path(p >= end || *p != ':')) {
+        return NULL;
+    }
+
+    p = njs_date_number_parse(&tm->tm_min, p + 1, end, 2);
+    if (nxt_slow_path(p == NULL)) {
+        return p;
+    }
+
+    if (nxt_slow_path(p >= end || *p != ':')) {
+        return NULL;
+    }
+
+    return njs_date_number_parse(&tm->tm_sec, p + 1, end, 2);
+}
+
+
+static nxt_noinline nxt_int_t
+njs_date_gmtoff_parse(const u_char *start, const u_char *end)
+{
+    int           gmtoff, hour, min;
+    const u_char  *p;
+
+    if (nxt_fast_path(start + 4 < end && (*start == '+' || *start == '-'))) {
+
+        p = njs_date_number_parse(&hour, start + 1, end, 2);
+        if (nxt_slow_path(p == NULL)) {
+            return -1;
+        }
+
+        p = njs_date_number_parse(&min, p, end, 2);
+        if (nxt_slow_path(p == NULL)) {
+            return -1;
+        }
+
+        gmtoff = hour * 60 + min;
+
+        if (*start == '-') {
+            gmtoff = -gmtoff;
+        }
+
+        return gmtoff;
+    }
+
+    return -1;
+}
+
+
+static nxt_noinline const u_char *
+njs_date_number_parse(int *value, const u_char *p, const u_char *end,
+    size_t size)
+{
+    u_char     c;
+    nxt_int_t  n;
+
+    n = 0;
+
+    do {
+        if (nxt_slow_path(p >= end)) {
+            return NULL;
+        }
+
+        c = *p++;
+
+        /* Values below '0' become >= 208. */
+        c = c - '0';
+
+        if (nxt_slow_path(c > 9)) {
+            return NULL;
+        }
+
+        n = n * 10 + c;
+
+        size--;
+
+    } while (size != 0);
+
+    *value = n;
+
+    return p;
+}
+
+
+static const njs_object_prop_t  njs_date_constructor_properties[] =
+{
+    /* Date.name == "Date". */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("Date"),
+    },
+
+    /* Date.length == 7. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 1, 7.0),
+    },
+
+    /* Date.prototype. */
+    {
+        .type = NJS_NATIVE_GETTER,
+        .name = njs_string("prototype"),
+        .value = njs_native_getter(njs_object_prototype_create),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("UTC"),
+        .value = njs_native_function(njs_date_utc, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("now"),
+        .value = njs_native_function(njs_date_now, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("parse"),
+        .value = njs_native_function(njs_date_parse, 0,
+                     NJS_SKIP_ARG, NJS_STRING_ARG),
+    },
+};
+
+
+const njs_object_init_t  njs_date_constructor_init = {
+    njs_date_constructor_properties,
+    nxt_nitems(njs_date_constructor_properties),
+};
+
+
+static njs_ret_t
+njs_date_prototype_value_of(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    njs_number_set(&vm->retval, args[0].data.u.date->time);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_to_string(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    return njs_date_string(vm, "%a %b %d %Y %T GMT%z (%Z)",
+                           args[0].data.u.date->time);
+}
+
+
+static njs_ret_t
+njs_date_prototype_to_date_string(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    return njs_date_string(vm, "%a %b %d %Y", args[0].data.u.date->time);
+}
+
+
+static njs_ret_t
+njs_date_prototype_to_time_string(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    return njs_date_string(vm, "%T GMT%z (%Z)", args[0].data.u.date->time);
+}
+
+
+static nxt_noinline njs_ret_t
+njs_date_string(njs_vm_t *vm, const char *fmt, double time)
+{
+    size_t     size;
+    time_t     clock;
+    u_char     *start;
+    u_char     buf[NJS_DATE_TIME_LEN];
+    struct tm  tm;
+
+    if (!njs_is_nan(time)) {
+        clock = time / 1000;
+        localtime_r(&clock, &tm);
+
+        size = strftime((char *) buf, NJS_DATE_TIME_LEN, fmt, &tm);
+
+        start = njs_string_alloc(vm, &vm->retval, size, size);
+
+        if (nxt_fast_path(start != NULL)) {
+            memcpy(start, buf, size);
+            return NXT_OK;
+        }
+
+        return NXT_ERROR;
+    }
+
+    vm->retval = njs_string_invalid_date;
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_to_utc_string(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double             time;
+    size_t             size;
+    time_t             clock;
+    u_char             *start;
+    u_char             buf[NJS_DATE_TIME_LEN];
+    struct tm          tm;
+
+    static const char  *week[] = { "Sun", "Mon", "Tue", "Wed",
+                                   "Thu", "Fri", "Sat" };
+
+    static const char  *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+                                    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+    time = args[0].data.u.date->time;
+
+    if (!njs_is_nan(time)) {
+        clock = time / 1000;
+        gmtime_r(&clock, &tm);
+
+        size = snprintf((char *) buf, NJS_DATE_TIME_LEN,
+                        "%s %s %02d %4d %02d:%02d:%02d GMT",
+                        week[tm.tm_wday], month[tm.tm_mon],
+                        tm.tm_mday, tm.tm_year + 1900,
+                        tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+        start = njs_string_alloc(vm, &vm->retval, size, size);
+
+        if (nxt_fast_path(start != NULL)) {
+            memcpy(start, buf, size);
+            return NXT_OK;
+        }
+
+        return NXT_ERROR;
+    }
+
+    vm->retval = njs_string_invalid_date;
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_to_iso_string(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    int32_t    year;
+    double     time;
+    size_t     size;
+    time_t     clock;
+    u_char     *start;
+    u_char     buf[NJS_ISO_DATE_TIME_LEN];
+    struct tm  tm;
+
+    time = args[0].data.u.date->time;
+
+    if (!njs_is_nan(time)) {
+        clock = time / 1000;
+
+        gmtime_r(&clock, &tm);
+
+        year = tm.tm_year + 1900;
+
+        size = snprintf((char *) buf, NJS_ISO_DATE_TIME_LEN,
+                        (year < 0) ? "%07d-%02d-%02dT%02d:%02d:%02d.%03dZ":
+                                     "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
+                        year, tm.tm_mon + 1, tm.tm_mday,
+                        tm.tm_hour, tm.tm_min, tm.tm_sec,
+                        (int) ((int64_t) time % 1000));
+
+        start = njs_string_alloc(vm, &vm->retval, size, size);
+
+        if (nxt_fast_path(start != NULL)) {
+            memcpy(start, buf, size);
+            return NXT_OK;
+        }
+
+        return NXT_ERROR;
+    }
+
+    vm->exception = &njs_exception_range_error;
+
+    return NXT_ERROR;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_full_year(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     value;
+    time_t     clock;
+    struct tm  tm;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        clock = value / 1000;
+        localtime_r(&clock, &tm);
+
+        value = tm.tm_year + 1900;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_utc_full_year(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     value;
+    time_t     clock;
+    struct tm  tm;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        clock = value / 1000;
+        gmtime_r(&clock, &tm);
+
+        value = tm.tm_year + 1900;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_month(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double     value;
+    time_t     clock;
+    struct tm  tm;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        clock = value / 1000;
+        localtime_r(&clock, &tm);
+
+        value = tm.tm_mon;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_utc_month(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     value;
+    time_t     clock;
+    struct tm  tm;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        clock = value / 1000;
+
+        gmtime_r(&clock, &tm);
+
+        value = tm.tm_mon;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_date(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double     value;
+    time_t     clock;
+    struct tm  tm;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        clock = value / 1000;
+        localtime_r(&clock, &tm);
+
+        value = tm.tm_mday;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_utc_date(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     value;
+    time_t     clock;
+    struct tm  tm;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        clock = value / 1000;
+        gmtime_r(&clock, &tm);
+
+        value = tm.tm_mday;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_day(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double     value;
+    time_t     clock;
+    struct tm  tm;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        clock = value / 1000;
+        localtime_r(&clock, &tm);
+
+        value = tm.tm_wday;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_utc_day(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     value;
+    time_t     clock;
+    struct tm  tm;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        clock = value / 1000;
+        gmtime_r(&clock, &tm);
+
+        value = tm.tm_wday;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_hours(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double     value;
+    time_t     clock;
+    struct tm  tm;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        clock = value / 1000;
+
+        localtime_r(&clock, &tm);
+
+        value = tm.tm_hour;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_utc_hours(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     value;
+    time_t     clock;
+    struct tm  tm;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        clock = value / 1000;
+        gmtime_r(&clock, &tm);
+
+        value = tm.tm_hour;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_minutes(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     value;
+    time_t     clock;
+    struct tm  tm;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        clock = value / 1000;
+
+        localtime_r(&clock, &tm);
+
+        value = tm.tm_min;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_utc_minutes(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     value;
+    time_t     clock;
+    struct tm  tm;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        clock = value / 1000;
+        gmtime_r(&clock, &tm);
+
+        value = tm.tm_min;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_seconds(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double  value;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        value = (int64_t) (value / 1000) % 60;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_milliseconds(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double  value;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        value = (int64_t) value % 1000;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_get_timezone_offset(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     value;
+    time_t     clock;
+    struct tm  tm;
+
+    value = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(value))) {
+        clock = value / 1000;
+        localtime_r(&clock, &tm);
+
+        value = - nxt_timezone(&tm) / 60;
+    }
+
+    njs_number_set(&vm->retval, value);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_set_time(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double  time;
+
+    time = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(time))) {
+
+        if (nargs > 1) {
+            time = args[1].data.u.number;
+
+        } else {
+            time = NJS_NAN;
+        }
+    }
+
+    args[0].data.u.date->time = time;
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_set_milliseconds(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double  time;
+
+    time = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(time))) {
+
+        if (nargs > 1) {
+            time = (int64_t) (time / 1000) * 1000 + args[1].data.u.number;
+
+        } else {
+            time = NJS_NAN;
+        }
+    }
+
+    args[0].data.u.date->time = time;
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_set_seconds(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double   time;
+    int64_t  sec, ms;
+
+    time = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(time))) {
+
+        if (nargs > 1) {
+            sec = args[1].data.u.number;
+            ms = (nargs > 2) ? args[2].data.u.number : (int64_t) time % 1000;
+
+            time = (int64_t) (time / 60000) * 60000 + sec * 1000 + ms;
+
+        } else {
+            time = NJS_NAN;
+        }
+    }
+
+    args[0].data.u.date->time = time;
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_set_minutes(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     time;
+    time_t     clock;
+    int64_t    ms;
+    struct tm  tm;
+
+    time = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(time))) {
+
+        if (nargs > 1) {
+            clock = time / 1000;
+            localtime_r(&clock, &tm);
+
+            tm.tm_min = args[1].data.u.number;
+
+            if (nargs > 2) {
+                tm.tm_sec = args[2].data.u.number;
+            }
+
+            ms = (nargs > 3) ? args[3].data.u.number : (int64_t) time % 1000;
+
+            time = njs_date_time(&tm, ms);
+
+        } else {
+            time = NJS_NAN;
+        }
+    }
+
+    args[0].data.u.date->time = time;
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_set_utc_minutes(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double   time;
+    int64_t  clock, min, sec, ms;
+
+    time = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(time))) {
+
+        if (nargs > 1) {
+            clock = time / 1000;
+
+            sec = (nargs > 2) ? args[2].data.u.number : clock % 60;
+            min = args[1].data.u.number;
+
+            clock = clock / 3600 * 3600 + min * 60 + sec;
+
+            ms = (nargs > 3) ? args[3].data.u.number : (int64_t) time % 1000;
+
+            time = clock * 1000 + ms;
+
+        } else {
+            time = NJS_NAN;
+        }
+    }
+
+    args[0].data.u.date->time = time;
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_set_hours(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double     time;
+    time_t     clock;
+    int64_t    ms;
+    struct tm  tm;
+
+    time = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(time))) {
+
+        if (nargs > 1) {
+            clock = time / 1000;
+            localtime_r(&clock, &tm);
+
+            tm.tm_hour = args[1].data.u.number;
+
+            if (nargs > 2) {
+                tm.tm_min = args[2].data.u.number;
+            }
+
+            if (nargs > 3) {
+                tm.tm_sec = args[3].data.u.number;
+            }
+
+            ms = (nargs > 4) ? args[4].data.u.number : (int64_t) time % 1000;
+
+            time = njs_date_time(&tm, ms);
+
+        } else {
+            time = NJS_NAN;
+        }
+    }
+
+    args[0].data.u.date->time = time;
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_set_utc_hours(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double   time;
+    int64_t  clock, hour, min, sec, ms;
+
+    time = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(time))) {
+
+        if (nargs > 1) {
+            clock = time / 1000;
+
+            sec = (nargs > 3) ? args[3].data.u.number : clock % 60;
+            min = (nargs > 2) ? args[2].data.u.number : clock / 60 % 60;
+            hour = args[1].data.u.number;
+
+            clock = clock / 86400 * 86400 + hour * 3600 + min * 60 + sec;
+
+            ms = (nargs > 4) ? args[4].data.u.number : (int64_t) time % 1000;
+
+            time = clock * 1000 + ms;
+
+        } else {
+            time = NJS_NAN;
+        }
+    }
+
+    args[0].data.u.date->time = time;
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_set_date(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double     time;
+    time_t     clock;
+    struct tm  tm;
+
+    time = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(time))) {
+
+        if (nargs > 1) {
+            clock = time / 1000;
+            localtime_r(&clock, &tm);
+
+            tm.tm_mday = args[1].data.u.number;
+
+            time = njs_date_time(&tm, (int64_t) time % 1000);
+
+        } else {
+            time = NJS_NAN;
+        }
+    }
+
+    args[0].data.u.date->time = time;
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_set_utc_date(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     time;
+    time_t     clock;
+    struct tm  tm;
+
+    time = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(time))) {
+
+        if (nargs > 1) {
+            clock = time / 1000;
+            gmtime_r(&clock, &tm);
+
+            tm.tm_mday = args[1].data.u.number;
+
+            time = njs_date_utc_time(&tm, time);
+
+        } else {
+            time = NJS_NAN;
+        }
+    }
+
+    args[0].data.u.date->time = time;
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_set_month(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    double     time;
+    time_t     clock;
+    struct tm  tm;
+
+    time = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(time))) {
+
+        if (nargs > 1) {
+            clock = time / 1000;
+            localtime_r(&clock, &tm);
+
+            tm.tm_mon = args[1].data.u.number;
+
+            if (nargs > 2) {
+                tm.tm_mday = args[2].data.u.number;
+            }
+
+            time = njs_date_time(&tm, (int64_t) time % 1000);
+
+        } else {
+            time = NJS_NAN;
+        }
+    }
+
+    args[0].data.u.date->time = time;
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_set_utc_month(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     time;
+    time_t     clock;
+    struct tm  tm;
+
+    time = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(time))) {
+
+        if (nargs > 1) {
+            clock = time / 1000;
+            gmtime_r(&clock, &tm);
+
+            tm.tm_mon = args[1].data.u.number;
+
+            if (nargs > 2) {
+                tm.tm_mday = args[2].data.u.number;
+            }
+
+            time = njs_date_utc_time(&tm, time);
+
+        } else {
+            time = NJS_NAN;
+        }
+    }
+
+    args[0].data.u.date->time = time;
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_set_full_year(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     time;
+    time_t     clock;
+    struct tm  tm;
+
+    time = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(time))) {
+
+        if (nargs > 1) {
+            clock = time / 1000;
+            localtime_r(&clock, &tm);
+
+            tm.tm_year = args[1].data.u.number - 1900;
+
+            if (nargs > 2) {
+                tm.tm_mon = args[2].data.u.number;
+            }
+
+            if (nargs > 3) {
+                tm.tm_mday = args[3].data.u.number;
+            }
+
+            time = njs_date_time(&tm, (int64_t) time % 1000);
+
+        } else {
+            time = NJS_NAN;
+        }
+    }
+
+    args[0].data.u.date->time = time;
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_date_prototype_set_utc_full_year(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    double     time;
+    time_t     clock;
+    struct tm  tm;
+
+    time = args[0].data.u.date->time;
+
+    if (nxt_fast_path(!njs_is_nan(time))) {
+
+        if (nargs > 1) {
+            clock = time / 1000;
+            gmtime_r(&clock, &tm);
+
+            tm.tm_year = args[1].data.u.number - 1900;
+
+            if (nargs > 2) {
+                tm.tm_mon = args[2].data.u.number;
+            }
+
+            if (nargs > 3) {
+                tm.tm_mday = args[3].data.u.number;
+            }
+
+            time = njs_date_utc_time(&tm, time);
+
+        } else {
+            time = NJS_NAN;
+        }
+    }
+
+    args[0].data.u.date->time = time;
+    njs_number_set(&vm->retval, time);
+
+    return NXT_OK;
+}
+
+
+static nxt_noinline double
+njs_date_time(struct tm *tm, int64_t ms)
+{
+    double  time;
+    time_t  clock;
+
+    tm->tm_isdst = -1;
+    clock = mktime(tm);
+
+    if (nxt_fast_path(clock != -1)) {
+        time = (int64_t) clock * 1000 + ms;
+
+    } else {
+        time = NJS_NAN;
+    }
+
+    return time;
+}
+
+
+static double
+njs_date_utc_time(struct tm *tm, double time)
+{
+    return njs_timegm(tm) * 1000 + (int64_t) time % 1000;
+}
+
+
+/*
+ * ECMAScript 5.1: call object method "toISOString".
+ * Date.toJSON() must be a continuation otherwise it may endlessly
+ * call Date.toISOString().
+ */
+
+static njs_ret_t
+njs_date_prototype_to_json(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t retval)
+{
+    njs_object_prop_t   *prop;
+    njs_continuation_t  *cont;
+    nxt_lvlhsh_query_t  lhq;
+
+    cont = (njs_continuation_t *) njs_continuation(vm->frame);
+    cont->function = njs_date_prototype_to_json_continuation;
+
+    if (njs_is_object(&args[0])) {
+        lhq.key_hash = NJS_TO_ISO_STRING_HASH;
+        lhq.key.len = sizeof("toISOString") - 1;
+        lhq.key.data = (u_char *) "toISOString";
+
+        prop = njs_object_property(vm, args[0].data.u.object, &lhq);
+
+        if (nxt_fast_path(prop != NULL && njs_is_function(&prop->value))) {
+            return njs_function_apply(vm, prop->value.data.u.function,
+                                      args, nargs, retval);
+        }
+    }
+
+    vm->exception = &njs_exception_type_error;
+
+    return NXT_ERROR;
+}
+
+
+static njs_ret_t
+njs_date_prototype_to_json_continuation(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t retval)
+{
+    /* Skip retval update. */
+    vm->frame->skip = 1;
+
+    return NXT_OK;
+}
+
+
+static const njs_object_prop_t  njs_date_prototype_properties[] =
+{
+    {
+        .type = NJS_NATIVE_GETTER,
+        .name = njs_string("__proto__"),
+        .value = njs_native_getter(njs_primitive_prototype_get_proto),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("valueOf"),
+        .value = njs_native_function(njs_date_prototype_value_of, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("toString"),
+        .value = njs_native_function(njs_date_prototype_to_string, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("toDateString"),
+        .value = njs_native_function(njs_date_prototype_to_date_string, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("toTimeString"),
+        .value = njs_native_function(njs_date_prototype_to_time_string, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("toLocaleString"),
+        .value = njs_native_function(njs_date_prototype_to_string, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("toLocaleDateString"),
+        .value = njs_native_function(njs_date_prototype_to_date_string, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("toLocaleTimeString"),
+        .value = njs_native_function(njs_date_prototype_to_time_string, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("toUTCString"),
+        .value = njs_native_function(njs_date_prototype_to_utc_string, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("toISOString"),
+        .value = njs_native_function(njs_date_prototype_to_iso_string, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getTime"),
+        .value = njs_native_function(njs_date_prototype_value_of, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getFullYear"),
+        .value = njs_native_function(njs_date_prototype_get_full_year, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getUTCFullYear"),
+        .value = njs_native_function(njs_date_prototype_get_utc_full_year,
+                     0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getMonth"),
+        .value = njs_native_function(njs_date_prototype_get_month, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getUTCMonth"),
+        .value = njs_native_function(njs_date_prototype_get_utc_month, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getDate"),
+        .value = njs_native_function(njs_date_prototype_get_date, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getUTCDate"),
+        .value = njs_native_function(njs_date_prototype_get_utc_date, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getDay"),
+        .value = njs_native_function(njs_date_prototype_get_day, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getUTCDay"),
+        .value = njs_native_function(njs_date_prototype_get_utc_day, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getHours"),
+        .value = njs_native_function(njs_date_prototype_get_hours, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getUTCHours"),
+        .value = njs_native_function(njs_date_prototype_get_utc_hours, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getMinutes"),
+        .value = njs_native_function(njs_date_prototype_get_minutes, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getUTCMinutes"),
+        .value = njs_native_function(njs_date_prototype_get_utc_minutes, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getSeconds"),
+        .value = njs_native_function(njs_date_prototype_get_seconds, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("getUTCSeconds"),
+        .value = njs_native_function(njs_date_prototype_get_seconds, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("getMilliseconds"),
+        .value = njs_native_function(njs_date_prototype_get_milliseconds, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("getUTCMilliseconds"),
+        .value = njs_native_function(njs_date_prototype_get_milliseconds, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("getTimezoneOffset"),
+        .value = njs_native_function(njs_date_prototype_get_timezone_offset,
+                     0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setTime"),
+        .value = njs_native_function(njs_date_prototype_set_time, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setMilliseconds"),
+        .value = njs_native_function(njs_date_prototype_set_milliseconds, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setUTCMilliseconds"),
+        .value = njs_native_function(njs_date_prototype_set_milliseconds, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setSeconds"),
+        .value = njs_native_function(njs_date_prototype_set_seconds, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setUTCSeconds"),
+        .value = njs_native_function(njs_date_prototype_set_seconds, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setMinutes"),
+        .value = njs_native_function(njs_date_prototype_set_minutes, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG,
+                     NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setUTCMinutes"),
+        .value = njs_native_function(njs_date_prototype_set_utc_minutes, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG,
+                     NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setHours"),
+        .value = njs_native_function(njs_date_prototype_set_hours, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG,
+                     NJS_NUMBER_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setUTCHours"),
+        .value = njs_native_function(njs_date_prototype_set_utc_hours, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG,
+                     NJS_NUMBER_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setDate"),
+        .value = njs_native_function(njs_date_prototype_set_date, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setUTCDate"),
+        .value = njs_native_function(njs_date_prototype_set_utc_date, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setMonth"),
+        .value = njs_native_function(njs_date_prototype_set_month, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setUTCMonth"),
+        .value = njs_native_function(njs_date_prototype_set_utc_month, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setFullYear"),
+        .value = njs_native_function(njs_date_prototype_set_full_year, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG,
+                     NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("setUTCFullYear"),
+        .value = njs_native_function(njs_date_prototype_set_utc_full_year, 0,
+                     NJS_DATE_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG,
+                     NJS_NUMBER_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_long_string("toJSON"),
+        .value = njs_native_function(njs_date_prototype_to_json,
+                     NJS_CONTINUATION_SIZE, 0),
+    },
+};
+
+
+const njs_object_init_t  njs_date_prototype_init = {
+    njs_date_prototype_properties,
+    nxt_nitems(njs_date_prototype_properties),
+};
diff --git a/njs/njs_date.h b/njs/njs_date.h
new file mode 100644 (file)
index 0000000..b590e2c
--- /dev/null
@@ -0,0 +1,25 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_DATE_H_INCLUDED_
+#define _NJS_DATE_H_INCLUDED_
+
+
+struct njs_date_s {
+    njs_object_t  object;
+    double        time;
+};
+
+
+njs_ret_t njs_date_constructor(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+
+
+extern const njs_object_init_t  njs_date_constructor_init;
+extern const njs_object_init_t  njs_date_prototype_init;
+
+
+#endif /* _NJS_DATE_H_INCLUDED_ */
index 266ae2140cf8992d16f67aef2d419768cef4c69f..2fb163fb4ad8ed59e96cb4ce8f2e1a47bc591dbd 100644 (file)
@@ -15,6 +15,7 @@
 #define NJS_STRING_ARG             5
 #define NJS_OBJECT_ARG             6
 #define NJS_REGEXP_ARG             7
+#define NJS_DATE_ARG               8
 
 
 struct njs_function_lambda_s {
index 42f28c944215475a775666f7a91f35c0e8ea9fe4..1db4029aafaece4ea71f0146dadeb1bc466594c2 100644 (file)
@@ -285,6 +285,7 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
     case NJS_TOKEN_STRING_CONSTRUCTOR:
     case NJS_TOKEN_FUNCTION_CONSTRUCTOR:
     case NJS_TOKEN_REGEXP_CONSTRUCTOR:
+    case NJS_TOKEN_DATE_CONSTRUCTOR:
     case NJS_TOKEN_EVAL:
     case NJS_TOKEN_EXTERNAL:
         return NXT_OK;
index dabf10a1400d3a33b2c6953daa10a7c580f9a65b..fbf5182596a64a5c92f65c2dc57bfe153b28e52a 100644 (file)
@@ -85,6 +85,7 @@ static const njs_keyword_t  njs_keywords[] = {
     { nxt_string("String"),        NJS_TOKEN_STRING_CONSTRUCTOR, 0 },
     { nxt_string("Function"),      NJS_TOKEN_FUNCTION_CONSTRUCTOR, 0 },
     { nxt_string("RegExp"),        NJS_TOKEN_REGEXP_CONSTRUCTOR, 0 },
+    { nxt_string("Date"),          NJS_TOKEN_DATE_CONSTRUCTOR, 0 },
     { nxt_string("eval"),          NJS_TOKEN_EVAL, 0 },
 
     /* Reserved words. */
index 57b1e71f0b4373e9a9c385f1ddddc2a68c78f5de..432b7258c1c6c909289c00b26187c56f6bf21bdb 100644 (file)
@@ -597,6 +597,8 @@ static const njs_value_t  njs_object_function_string =
                                      njs_long_string("[object Function]");
 static const njs_value_t  njs_object_regexp_string =
                                      njs_long_string("[object RegExp]");
+static const njs_value_t  njs_object_date_string =
+                                     njs_long_string("[object Date]");
 
 
 njs_ret_t
@@ -626,6 +628,7 @@ njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
         &njs_object_string_string,
         &njs_object_function_string,
         &njs_object_regexp_string,
+        &njs_object_date_string,
     };
 
     index = args[0].type;
index 6a5545800adf4f0956afc936ac479e0df58f74dd..0f25e1d8cbe4ae0d06b5880a0f7098249f03cd6d 100644 (file)
         'v'), 'a'), 'l'), 'u'), 'e'), 'O'), 'f')
 
 
+#define NJS_TO_ISO_STRING_HASH                                                \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(NXT_DJB_HASH_INIT,                                       \
+        't'), 'o'), 'I'), 'S'), 'O'), 'S'), 't'), 'r'), 'i'), 'n'), 'g')
+
+
 #endif /* _NJS_OBJECT_HASH_H_INCLUDED_ */
index c858f96ae52d712f5779370f4a09684bf61eeafa..be0781c945ba2fa33d64d4f427a56cb0185928b9 100644 (file)
@@ -1622,6 +1622,10 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
         node->index = NJS_INDEX_REGEXP;
         break;
 
+    case NJS_TOKEN_DATE_CONSTRUCTOR:
+        node->index = NJS_INDEX_DATE;
+        break;
+
     case NJS_TOKEN_EVAL:
         node->index = NJS_INDEX_EVAL;
         break;
index cd16e335cf8e0001b58573c28fd5d544e36a0bae..b11e7b63824bf991424effe1a5a0edb70b7c0fc2 100644 (file)
@@ -165,11 +165,12 @@ typedef enum {
 
     NJS_TOKEN_OBJECT_CONSTRUCTOR,
     NJS_TOKEN_ARRAY_CONSTRUCTOR,
-    NJS_TOKEN_FUNCTION_CONSTRUCTOR,
-    NJS_TOKEN_REGEXP_CONSTRUCTOR,
     NJS_TOKEN_BOOLEAN_CONSTRUCTOR,
     NJS_TOKEN_NUMBER_CONSTRUCTOR,
     NJS_TOKEN_STRING_CONSTRUCTOR,
+    NJS_TOKEN_FUNCTION_CONSTRUCTOR,
+    NJS_TOKEN_REGEXP_CONSTRUCTOR,
+    NJS_TOKEN_DATE_CONSTRUCTOR,
     NJS_TOKEN_EVAL,
 
     NJS_TOKEN_RESERVED,
index df32542b3f92381219d699567fb846e347a9e685..f5ce0f72da8c0bf5da23ecd3d4f5af27d1a06f1a 100644 (file)
@@ -963,6 +963,7 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
     case NJS_OBJECT_NUMBER:
     case NJS_OBJECT_STRING:
     case NJS_REGEXP:
+    case NJS_DATE:
         obj = object->data.u.object;
         break;
 
@@ -2465,6 +2466,13 @@ njs_normalize_args(njs_vm_t *vm, njs_value_t *args, uint8_t *args_types,
 
             break;
 
+        case NJS_DATE_ARG:
+            if (!njs_is_date(args)) {
+                goto type_error;
+            }
+
+            break;
+
         case NJS_OBJECT_ARG:
 
             if (njs_is_null_or_void(args)) {
index aa52808f17002f30cdcd072639eb3d77cfaad619..068bea9a544217b94c1ed24f09480f37b6586397 100644 (file)
@@ -95,6 +95,7 @@ typedef enum {
     NJS_OBJECT_STRING   = 0x0c,
     NJS_FUNCTION        = 0x0d,
     NJS_REGEXP          = 0x0e,
+    NJS_DATE            = 0x0f,
 } njs_value_type_t;
 
 
@@ -112,6 +113,7 @@ typedef struct njs_array_s            njs_array_t;
 typedef struct njs_function_lambda_s  njs_function_lambda_t;
 typedef struct njs_regexp_s           njs_regexp_t;
 typedef struct njs_regexp_pattern_s   njs_regexp_pattern_t;
+typedef struct njs_date_s             njs_date_t;
 typedef struct njs_extern_s           njs_extern_t;
 typedef struct njs_native_frame_s     njs_native_frame_t;
 typedef struct njs_property_next_s    njs_property_next_t;
@@ -133,7 +135,7 @@ struct njs_object_s {
 };
 
 
-#define NJS_ARGS_TYPES_MAX            3
+#define NJS_ARGS_TYPES_MAX            5
 
 struct njs_function_s {
     njs_object_t                      object;
@@ -215,6 +217,7 @@ union njs_value_s {
             njs_function_t             *function;
             njs_function_lambda_t      *lambda;
             njs_regexp_t               *regexp;
+            njs_date_t                 *date;
             njs_getter_t               getter;
             njs_extern_t               *external;
             njs_value_t                *value;
@@ -372,6 +375,10 @@ typedef njs_ret_t (*njs_vmcode_operation_t)(njs_vm_t *vm, njs_value_t *value1,
     ((value)->type == NJS_REGEXP)
 
 
+#define njs_is_date(value)                                                    \
+    ((value)->type == NJS_DATE)
+
+
 #define njs_is_external(value)                                                \
     ((value)->type == NJS_EXTERNAL)
 
@@ -664,7 +671,8 @@ enum njs_prototypes_e {
     NJS_PROTOTYPE_STRING,
     NJS_PROTOTYPE_FUNCTION,
     NJS_PROTOTYPE_REGEXP,
-#define NJS_PROTOTYPE_MAX    (NJS_PROTOTYPE_REGEXP + 1)
+    NJS_PROTOTYPE_DATE,
+#define NJS_PROTOTYPE_MAX    (NJS_PROTOTYPE_DATE + 1)
 };
 
 
@@ -680,6 +688,7 @@ enum njs_functions_e {
     NJS_FUNCTION_STRING =    NJS_PROTOTYPE_STRING,
     NJS_FUNCTION_FUNCTION =  NJS_PROTOTYPE_FUNCTION,
     NJS_FUNCTION_REGEXP =    NJS_PROTOTYPE_REGEXP,
+    NJS_FUNCTION_DATE =      NJS_PROTOTYPE_DATE,
 
     NJS_FUNCTION_EVAL,
 #define NJS_FUNCTION_MAX     (NJS_FUNCTION_EVAL + 1)
@@ -706,6 +715,7 @@ enum njs_object_e {
 #define NJS_INDEX_STRING         njs_global_scope_index(NJS_FUNCTION_STRING)
 #define NJS_INDEX_FUNCTION       njs_global_scope_index(NJS_FUNCTION_FUNCTION)
 #define NJS_INDEX_REGEXP         njs_global_scope_index(NJS_FUNCTION_REGEXP)
+#define NJS_INDEX_DATE           njs_global_scope_index(NJS_FUNCTION_DATE)
 #define NJS_INDEX_EVAL           njs_global_scope_index(NJS_FUNCTION_EVAL)
 
 #define NJS_INDEX_GLOBAL_RETVAL  njs_global_scope_index(NJS_FUNCTION_MAX)
index 55c7865abde20e9ee02551dec4ea3a6849791533..44ef4d5ed509f8bc20fb7de75914bedadcb7a1c1 100644 (file)
@@ -15,6 +15,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <sys/resource.h>
+#include <time.h>
 
 
 typedef struct {
@@ -3916,6 +3917,303 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var o = Object.create(null); '__proto__' in o"),
       nxt_string("false") },
 
+    { nxt_string("var d = new Date(1308895200000); d.getTime()"),
+      nxt_string("1308895200000") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.getTime()"),
+      nxt_string("1308895200000") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.valueOf()"),
+      nxt_string("1308895200000") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d"),
+      nxt_string("Fri Jun 24 2011 18:45:00 GMT+1245 (CHAST)") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.toString()"),
+      nxt_string("Fri Jun 24 2011 18:45:00 GMT+1245 (CHAST)") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.toDateString()"),
+      nxt_string("Fri Jun 24 2011") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.toTimeString()"),
+      nxt_string("18:45:00 GMT+1245 (CHAST)") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.toUTCString()"),
+      nxt_string("Fri Jun 24 2011 06:00:00 GMT") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45, 12, 625);"
+                 "d.toISOString()"),
+      nxt_string("2011-06-24T06:00:12.625Z") },
+
+#if 0
+    /* These tests fail on Solaris: gmtime_r() returns off by one day. */
+
+    { nxt_string("var d = new Date(-62167219200000); d.toISOString()"),
+      nxt_string("0000-01-01T00:00:00.000Z") },
+
+    { nxt_string("var d = new Date(-62135596800000); d.toISOString()"),
+      nxt_string("0001-01-01T00:00:00.000Z") },
+
+    { nxt_string("var d = new Date(-62198755200000); d.toISOString()"),
+      nxt_string("-000001-01-01T00:00:00.000Z") },
+#endif
+
+    { nxt_string("Date.UTC(2011, 5, 24, 6, 0)"),
+      nxt_string("1308895200000") },
+
+    { nxt_string("Date.parse()"),
+      nxt_string("NaN") },
+
+    { nxt_string("Date.parse('2011-06-24T06:01:02.625Z')"),
+      nxt_string("1308895262625") },
+
+    { nxt_string("Date.parse('Fri, 24 Jun 2011 18:48:02 GMT')"),
+      nxt_string("1308941282000") },
+
+    { nxt_string("Date.parse('Fri, 24 Jun 2011 18:48:02 +1245')"),
+      nxt_string("1308895382000") },
+
+    { nxt_string("Date.parse('Fri Jun 24 2011 18:48:02 GMT+1245')"),
+      nxt_string("1308895382000") },
+
+    /* Jan 1, 1. */
+    { nxt_string("Date.parse('+000001-01-01T00:00:00.000Z')"),
+      nxt_string("-62135596800000") },
+
+    /* Mar 2, 1 BCE. */
+    { nxt_string("Date.parse('+000000-03-02T00:00:00.000Z')"),
+      nxt_string("-62161948800000") },
+
+    /* Mar 1, 1 BCE. */
+    { nxt_string("Date.parse('+000000-03-01T00:00:00.000Z')"),
+      nxt_string("-62162035200000") },
+
+    /* Feb 29, 1 BCE. */
+    { nxt_string("Date.parse('+000000-02-29T00:00:00.000Z')"),
+      nxt_string("-62162121600000") },
+
+    /* Feb 28, 1 BCE. */
+    { nxt_string("Date.parse('+000000-02-28T00:00:00.000Z')"),
+      nxt_string("-62162208000000") },
+
+    /* Jan 1, 1 BCE. */
+    { nxt_string("Date.parse('+000000-01-01T00:00:00.000Z')"),
+      nxt_string("-62167219200000") },
+
+    /* Jan 1, 2 BCE. */
+    { nxt_string("Date.parse('-000001-01-01T00:00:00.000Z')"),
+      nxt_string("-62198755200000") },
+
+    { nxt_string("var d = new Date(); d == Date.parse(d.toString())"),
+      nxt_string("true") },
+
+    { nxt_string("var s = Date(); s === Date(Date.parse(s))"),
+      nxt_string("true") },
+
+    { nxt_string("var n = Date.now(); n == new Date(n)"),
+      nxt_string("true") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.getFullYear()"),
+      nxt_string("2011") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.getUTCFullYear()"),
+      nxt_string("2011") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.getMonth()"),
+      nxt_string("5") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.getUTCMonth()"),
+      nxt_string("5") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.getDate()"),
+      nxt_string("24") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.getUTCDate()"),
+      nxt_string("24") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.getDay()"),
+      nxt_string("5") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.getUTCDay()"),
+      nxt_string("5") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.getHours()"),
+      nxt_string("18") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.getUTCHours()"),
+      nxt_string("6") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.getMinutes()"),
+      nxt_string("45") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.getUTCMinutes()"),
+      nxt_string("0") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45, 12);"
+                 "d.getSeconds()"),
+      nxt_string("12") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45, 12);"
+                 "d.getUTCSeconds()"),
+      nxt_string("12") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45, 12, 625);"
+                 "d.getMilliseconds()"),
+      nxt_string("625") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45, 12, 625);"
+                 "d.getUTCMilliseconds()"),
+      nxt_string("625") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45, 12, 625);"
+                 "d.getTimezoneOffset()"),
+      nxt_string("-765") },
+
+    { nxt_string("var d = new Date(); d.setTime(1308895200000); d.getTime()"),
+      nxt_string("1308895200000") },
+
+    { nxt_string("var d = new Date(1308895201625); d.setMilliseconds(5003);"
+                 "d.getTime()"),
+      nxt_string("1308895206003") },
+
+    { nxt_string("var d = new Date(1308895201625); d.setSeconds(2, 5003);"
+                 "d.getTime()"),
+      nxt_string("1308895207003") },
+
+    { nxt_string("var d = new Date(1308895201625); d.setSeconds(2);"
+                 "d.getTime()"),
+      nxt_string("1308895202625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setMinutes(3, 2, 5003);"
+                 "d.getTime()"),
+      nxt_string("1308892687003") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setMinutes(3, 2);"
+                 "d.getTime()"),
+      nxt_string("1308892682625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setMinutes(3);"
+                 "d.getTime()"),
+      nxt_string("1308892683625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setUTCMinutes(3, 2, 5003);"
+                 "d.getTime()"),
+      nxt_string("1308895387003") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setUTCMinutes(3, 2);"
+                 "d.getTime()"),
+      nxt_string("1308895382625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setUTCMinutes(3);"
+                 "d.getTime()"),
+      nxt_string("1308895383625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setHours(20, 3, 2, 5003);"
+                 "d.getTime()"),
+      nxt_string("1308899887003") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setHours(20, 3, 2);"
+                 "d.getTime()"),
+      nxt_string("1308899882625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setHours(20, 3);"
+                 "d.getTime()"),
+      nxt_string("1308899883625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setHours(20);"
+                 "d.getTime()"),
+      nxt_string("1308902523625") },
+
+    { nxt_string("var d = new Date(1308895323625);"
+                 "d.setUTCHours(20, 3, 2, 5003); d.getTime()"),
+      nxt_string("1308945787003") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setUTCHours(20, 3, 2);"
+                 "d.getTime()"),
+      nxt_string("1308945782625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setUTCHours(20, 3);"
+                 "d.getTime()"),
+      nxt_string("1308945783625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setUTCHours(20);"
+                 "d.getTime()"),
+      nxt_string("1308945723625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setDate(10);"
+                 "d.getTime()"),
+      nxt_string("1307685723625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setUTCDate(10);"
+                 "d.getTime()"),
+      nxt_string("1307685723625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setMonth(2, 10);"
+                 "d.getTime()"),
+      nxt_string("1299733323625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setUTCMonth(2, 10);"
+                 "d.getTime()"),
+      nxt_string("1299736923625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setMonth(2);"
+                 "d.getTime()"),
+      nxt_string("1300942923625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setUTCMonth(2);"
+                 "d.getTime()"),
+      nxt_string("1300946523625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setFullYear(2010, 2, 10);"
+                 "d.getTime()"),
+      nxt_string("1268197323625") },
+
+    { nxt_string("var d = new Date(1308895323625);"
+                 "d.setUTCFullYear(2010, 2, 10); d.getTime()"),
+      nxt_string("1268200923625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setFullYear(2010, 2);"
+                 "d.getTime()"),
+      nxt_string("1269406923625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setUTCFullYear(2010, 2);"
+                 "d.getTime()"),
+      nxt_string("1269410523625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setFullYear(2010);"
+                 "d.getTime()"),
+      nxt_string("1277359323625") },
+
+    { nxt_string("var d = new Date(1308895323625); d.setUTCFullYear(2010);"
+                 "d.getTime()"),
+      nxt_string("1277359323625") },
+
+    { nxt_string("var d = new Date(2011, 5, 24, 18, 45, 12, 625);"
+                 "d.toJSON(1)"),
+      nxt_string("2011-06-24T06:00:12.625Z") },
+
+    { nxt_string("var o = { toISOString: function() { return 'OK' } }"
+                 "Date.prototype.toJSON.call(o, 1)"),
+      nxt_string("OK") },
+
+    { nxt_string("Date.name"),
+      nxt_string("Date") },
+
+    { nxt_string("Date.length"),
+      nxt_string("7") },
+
+    { nxt_string("Date.__proto__ === Function.prototype"),
+      nxt_string("true") },
+
+    { nxt_string("Date.prototype.constructor === Date"),
+      nxt_string("true") },
+
+    { nxt_string("Date.prototype.__proto__ === Object.prototype"),
+      nxt_string("true") },
+
+    { nxt_string("Date.constructor === Function"),
+      nxt_string("true") },
+
     /* eval(). */
 
     { nxt_string("eval.name"),
@@ -4299,6 +4597,13 @@ njs_unit_test(nxt_bool_t disassemble)
     njs_opaque_value_t    value;
     nxt_mem_cache_pool_t  *mcp;
 
+    /*
+     * Chatham Islands NZ-CHAT time zone.
+     * Standard time: UTC+12:45, Daylight Saving time: UTC+13:45.
+     */
+    (void) putenv((char *) "TZ=Pacific/Chatham");
+    tzset();
+
     shared = NULL;
 
     mcp = nxt_mem_cache_pool_create(&njs_mem_cache_pool_proto, NULL, NULL,
index fdb93d03524090107854560017190eb942b69fe1..a4b828dd575dc391b4982f856d3df14bc11e4818 100755 (executable)
@@ -51,6 +51,7 @@ END
 
 . ${NXT_AUTO}os
 . ${NXT_AUTO}clang
+. ${NXT_AUTO}time
 . ${NXT_AUTO}memalign
 . ${NXT_AUTO}getrandom
 . ${NXT_AUTO}pcre
diff --git a/nxt/auto/time b/nxt/auto/time
new file mode 100644 (file)
index 0000000..5559a75
--- /dev/null
@@ -0,0 +1,39 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) NGINX, Inc.
+
+
+# Linux, FreeBSD, MacOSX.
+
+nxt_feature="struct tm.tm_gmtoff"
+nxt_feature_name=NXT_HAVE_TM_GMTOFF
+nxt_feature_run=
+nxt_feature_incs=
+nxt_feature_libs=
+nxt_feature_test="#include <time.h>
+
+                  int main() {
+                      time_t     t;
+                      struct tm  tm;
+
+                      t = 0;
+                      localtime_r(&t, &tm);
+                      return tm.tm_gmtoff;
+                  }"
+. ${NXT_AUTO}feature
+
+
+# Solaris
+
+nxt_feature="altzone"
+nxt_feature_name=NXT_HAVE_ALTZONE
+nxt_feature_run=
+nxt_feature_incs=
+nxt_feature_libs=
+nxt_feature_test="#include <time.h>
+
+                  int main() {
+                      altzone = 0;
+                      return 0;
+                  }"
+. ${NXT_AUTO}feature