From: Igor Sysoev Date: Fri, 8 Apr 2016 15:19:43 +0000 (+0300) Subject: Date() function. X-Git-Tag: 0.1.0~38 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=b0f32ede42c3d82932c69c4ac91d2c662a29eba6;p=njs.git Date() function. --- diff --git a/Makefile b/Makefile index cf02682d..e12f9ed9 100644 --- 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 \ diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c index 453d74f5..8344f9c2 100644 --- a/njs/njs_builtin.c +++ b/njs/njs_builtin.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -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 index 00000000..5e8e3f9b --- /dev/null +++ b/njs/njs_date.c @@ -0,0 +1,2104 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * 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 index 00000000..b590e2c4 --- /dev/null +++ b/njs/njs_date.h @@ -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_ */ diff --git a/njs/njs_function.h b/njs/njs_function.h index 266ae214..2fb163fb 100644 --- a/njs/njs_function.h +++ b/njs/njs_function.h @@ -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 { diff --git a/njs/njs_generator.c b/njs/njs_generator.c index 42f28c94..1db4029a 100644 --- a/njs/njs_generator.c +++ b/njs/njs_generator.c @@ -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; diff --git a/njs/njs_lexer_keyword.c b/njs/njs_lexer_keyword.c index dabf10a1..fbf51825 100644 --- a/njs/njs_lexer_keyword.c +++ b/njs/njs_lexer_keyword.c @@ -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. */ diff --git a/njs/njs_object.c b/njs/njs_object.c index 57b1e71f..432b7258 100644 --- a/njs/njs_object.c +++ b/njs/njs_object.c @@ -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; diff --git a/njs/njs_object_hash.h b/njs/njs_object_hash.h index 6a554580..0f25e1d8 100644 --- a/njs/njs_object_hash.h +++ b/njs/njs_object_hash.h @@ -85,4 +85,19 @@ '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_ */ diff --git a/njs/njs_parser.c b/njs/njs_parser.c index c858f96a..be0781c9 100644 --- a/njs/njs_parser.c +++ b/njs/njs_parser.c @@ -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; diff --git a/njs/njs_parser.h b/njs/njs_parser.h index cd16e335..b11e7b63 100644 --- a/njs/njs_parser.h +++ b/njs/njs_parser.h @@ -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, diff --git a/njs/njs_vm.c b/njs/njs_vm.c index df32542b..f5ce0f72 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -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)) { diff --git a/njs/njs_vm.h b/njs/njs_vm.h index aa52808f..068bea9a 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -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) diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index 55c7865a..44ef4d5e 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -15,6 +15,7 @@ #include #include #include +#include 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, diff --git a/nxt/auto/configure b/nxt/auto/configure index fdb93d03..a4b828dd 100755 --- a/nxt/auto/configure +++ b/nxt/auto/configure @@ -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 index 00000000..5559a755 --- /dev/null +++ b/nxt/auto/time @@ -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 + + 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 + + int main() { + altzone = 0; + return 0; + }" +. ${NXT_AUTO}feature