/
niceSOFT
/
njs
Обзор
Документация
Войти
/
niceSOFT
/
njs
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/njs_date.c
1 727 строк
41 KB
Dmitry Volyntsev
Fixed --debug=YES build after
d4cedc0
.
18 ноя 2025, 05:56
18 ноя 2025, 05:56
fc526af
Код
Авторство
О чём код?
/* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include <njs_main.h> #define NJS_DATE_TIME_LEN \ sizeof("Mon Sep 28 1970 12:00:00 GMT+0600 (XXXXX)") #define NJS_DATE_MAX_FIELDS 8 #define NJS_DATE_WDAY 0 #define NJS_DATE_YR 1 #define NJS_DATE_MON 2 #define NJS_DATE_DAY 3 #define NJS_DATE_HR 4 #define NJS_DATE_MIN 5 #define NJS_DATE_SEC 6 #define NJS_DATE_MSEC 7 #if (NJS_HAVE_TM_GMTOFF) #define njs_timezone(tm) \ ((tm)->tm_gmtoff) #elif (NJS_HAVE_ALTZONE) #define njs_timezone(tm) \ (-(((tm)->tm_isdst > 0) ? altzone : timezone)) #endif #define njs_date_magic(field, local) \ ((local << 6) + field) #define njs_date_magic2(since, len, local) \ ((local << 6) + ((len & 7) << 3) + since) typedef enum { NJS_DATE_FMT_TO_TIME_STRING, NJS_DATE_FMT_TO_DATE_STRING, NJS_DATE_FMT_TO_STRING, NJS_DATE_FMT_TO_UTC_STRING, NJS_DATE_FMT_TO_ISO_STRING, } njs_date_fmt_t; static double njs_date_string_parse(njs_vm_t *vm, njs_value_t *date); static double njs_date_rfc2822_string_parse(int64_t tm[], const u_char *p, const u_char *end); static double njs_date_js_string_parse(int64_t tm[], const u_char *p, const u_char *end); 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 njs_int_t njs_date_month_parse(const u_char *p, const u_char *end); static const u_char *njs_date_time_parse(int64_t tm[], const u_char *p, const u_char *end); static int64_t njs_date_gmtoff_parse(const u_char *start, const u_char *end); static const u_char *njs_date_number_parse(int64_t *value, const u_char *p, const u_char *end, size_t size); static njs_int_t njs_date_string(njs_vm_t *vm, njs_value_t *retval, njs_date_fmt_t fmt, double time); njs_inline int64_t njs_mod(int64_t a, int64_t b) { int64_t m; m = a % b; return m + (m < 0) * b; } njs_inline int64_t njs_floor_div(int64_t a, int64_t b) { int64_t m; m = a % b; return (a - (m + (m < 0) * b)) / b; } njs_inline uint64_t njs_gettime(void) { struct timeval tv; gettimeofday(&tv, NULL); return (uint64_t) tv.tv_sec * 1000 + tv.tv_usec / 1000; } njs_inline double njs_timeclip(double time) { if (time < -8.64e15 || time > 8.64e15) { return NAN; } return trunc(time) + 0.0; } njs_inline int64_t njs_days_in_year(int64_t y) { return 365 + !(y % 4) - !(y % 100) + !(y % 400); } njs_inline int64_t njs_days_from_year(int64_t y) { return 365 * (y - 1970) + njs_floor_div(y - 1969, 4) - njs_floor_div(y - 1901, 100) + njs_floor_div(y - 1601, 400); } njs_inline double njs_make_day(int64_t yr, int64_t month, int64_t date) { double days; int64_t i, ym, mn, md; static const int min_year = -271821; static const int max_year = 275760; static const int month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; if (yr < min_year || yr > max_year || month < (min_year * 12) || month > (max_year * 12) || date < (min_year * 12 * 366) || date > (max_year * 12 * 366)) { return NAN; } mn = njs_mod(month, 12); ym = yr + (month - mn) / 12; days = njs_days_from_year(ym); for (i = 0; i < mn; i++) { md = month_days[i]; if (i == 1) { /* Leap day. */ md += njs_days_in_year(ym) - 365; } days += md; } return days + date - 1; } njs_inline int64_t njs_tz_offset(int64_t time) { time_t ti; struct tm tm; time /= 1000; #if (NJS_TIME_T_SIZE < 8) /* Smart truncation. */ if ((time_t) -1 < 0) { if (time < INT32_MIN) { time = INT32_MIN; } else if (time > INT32_MAX) { time = INT32_MAX; } } else { if (time < 0) { time = 0; } else if (time > UINT32_MAX) { time = UINT32_MAX; } } #endif ti = time; localtime_r(&ti, &tm); /* * As njs_timezone(&tm) may return value which is not a multiple of 60 * secs (see "zdump -v /etc/localtime" for MSK zone) rounding it to * minutes precision here to ensure: * var date = new Date(<args>) * date.valueOf() - date.getTimezoneOffset() * 60000 == Date.UTC(<args>) * which is expected by test262. */ return -njs_timezone(&tm) / 60; } njs_inline int64_t njs_year_from_days(int64_t *days) { int64_t y, d1, nd, d; d = *days; y = njs_floor_div(d * 10000, 3652425) + 1970; for ( ;; ) { d1 = d - njs_days_from_year(y); if (d1 < 0) { y--; } else { nd = njs_days_in_year(y); if (d1 < nd) { break; } y++; } } *days = d1; return y; } njs_inline double njs_make_date(int64_t tm[], njs_bool_t local) { double time, days; days = njs_make_day(tm[NJS_DATE_YR], tm[NJS_DATE_MON], tm[NJS_DATE_DAY]); if (njs_slow_path(isnan(days))) { return NAN; } time = ((tm[NJS_DATE_HR] * 60.0 + tm[NJS_DATE_MIN]) * 60.0 + tm[NJS_DATE_SEC]) * 1000.0 + tm[NJS_DATE_MSEC]; time += days * 86400000.0; if (time < -8.64e15 || time > 8.64e15) { return NAN; } if (local) { time += njs_tz_offset(time) * 60000; } return njs_timeclip(time); } njs_inline int64_t njs_destruct_date(double time, int64_t tm[], int index, njs_bool_t local) { int64_t days, wd, y, i, md, h, m, s, ms; static const int month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; if (njs_slow_path(isnan(time))) { time = 0; } else if (local) { time -= njs_tz_offset(time) * 60000; } h = njs_mod(time, 86400000); days = (time - h) / 86400000; ms = h % 1000; h = (h - ms) / 1000; s = h % 60; h = (h - s) / 60; m = h % 60; h = (h - m) / 60; wd = njs_mod(days + 4, 7); y = njs_year_from_days(&days); for (i = 0; i < 11; i++) { md = month_days[i]; if (i == 1) { /* Leap day. */ md += njs_days_in_year(y) - 365; } if (days < md) { break; } days -= md; } tm[NJS_DATE_YR] = y; tm[NJS_DATE_MON] = i; tm[NJS_DATE_DAY] = days + 1; tm[NJS_DATE_HR] = h; tm[NJS_DATE_MIN] = m; tm[NJS_DATE_SEC] = s; tm[NJS_DATE_MSEC] = ms; tm[NJS_DATE_WDAY] = wd; return tm[index]; } static njs_int_t njs_date_args(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, int64_t tm[]) { double num; njs_int_t ret; njs_uint_t i, n; njs_memzero(tm, NJS_DATE_MAX_FIELDS * sizeof(int64_t)); tm[NJS_DATE_DAY] = 1; n = njs_min(8, nargs); for (i = 1; i < n; i++) { ret = njs_value_to_number(vm, &args[i], &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!isfinite(num)) { tm[NJS_DATE_YR] = INT64_MIN; continue; } tm[i] = njs_number_to_integer(num); } if (tm[NJS_DATE_YR] >= 0 && tm[NJS_DATE_YR] < 100) { tm[NJS_DATE_YR] += 1900; } return NJS_OK; } njs_date_t * njs_date_alloc(njs_vm_t *vm, double time) { njs_date_t *date; date = njs_mp_alloc(vm->mem_pool, sizeof(njs_date_t)); if (njs_slow_path(date == NULL)) { njs_memory_error(vm); return NULL; } njs_flathsh_init(&date->object.hash); njs_flathsh_init(&date->object.shared_hash); date->object.type = NJS_DATE; date->object.shared = 0; date->object.extensible = 1; date->object.error_data = 0; date->object.fast_array = 0; date->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_DATE); date->object.slots = NULL; date->time = time; return date; } static njs_int_t njs_date_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double time; njs_int_t ret; njs_date_t *date; int64_t tm[NJS_DATE_MAX_FIELDS]; if (!vm->top_frame->ctor) { return njs_date_string(vm, retval, NJS_DATE_FMT_TO_STRING, njs_gettime()); } if (nargs == 1) { time = njs_gettime(); } else if (nargs == 2) { if (njs_is_object(&args[1])) { if (!njs_is_date(&args[1])) { ret = njs_value_to_primitive(vm, &args[1], &args[1], NJS_HINT_NONE); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } if (njs_is_date(&args[1])) { time = njs_date(&args[1])->time; } else if (njs_is_string(&args[1])) { time = njs_date_string_parse(vm, &args[1]); } else { time = njs_timeclip(njs_number(&args[1])); } } else { ret = njs_date_args(vm, args, nargs, tm); if (njs_slow_path(ret != NJS_OK)) { return ret; } time = njs_make_date(tm, 1); } date = njs_date_alloc(vm, time); if (njs_slow_path(date == NULL)) { return NJS_ERROR; } njs_set_date(retval, date); return NJS_OK; } static njs_int_t njs_date_utc(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double time; njs_int_t ret; int64_t tm[NJS_DATE_MAX_FIELDS]; time = NAN; if (nargs > 1) { ret = njs_date_args(vm, args, nargs, tm); if (njs_slow_path(ret != NJS_OK)) { return ret; } time = njs_make_date(tm, 0); } njs_set_number(retval, time); return NJS_OK; } static njs_int_t njs_date_now(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_set_number(retval, njs_gettime()); return NJS_OK; } static njs_int_t njs_date_parse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double time; njs_int_t ret; if (nargs > 1) { if (njs_slow_path(!njs_is_string(&args[1]))) { ret = njs_value_to_string(vm, &args[1], &args[1]); if (njs_slow_path(ret != NJS_OK)) { return ret; } } time = njs_date_string_parse(vm, &args[1]); } else { time = NAN; } njs_set_number(retval, time); return NJS_OK; } static int64_t njs_date_utc_offset_parse(const u_char *start, const u_char *end) { int64_t utc_off, hour, min; const u_char *p; if (njs_fast_path(start + 2 < end && (*start == '+' || *start == '-'))) { p = njs_date_number_parse(&hour, start + 1, end, 2); if (njs_slow_path(p == NULL || hour > 23)) { return -1; } if (p < end && *p == ':') { p++; } p = njs_date_number_parse(&min, p, end, 2); if (njs_slow_path(p == NULL || min > 59)) { return -1; } utc_off = hour * 60 + min; if (*start == '-') { utc_off = -utc_off; } return utc_off; } return -1; } static double njs_date_string_parse(njs_vm_t *vm, njs_value_t *date) { size_t ms_length; int64_t ext, utc_off; njs_str_t string; njs_bool_t sign, week, utc; const u_char *p, *next, *end; int64_t tm[NJS_DATE_MAX_FIELDS]; njs_string_get(vm, date, &string); p = string.start; end = p + string.length; if (njs_slow_path(p >= end)) { return NAN; } if (*p == '+' || *p == '-') { p++; sign = 1; } else { sign = 0; } tm[NJS_DATE_MON] = 0; tm[NJS_DATE_DAY] = 1; tm[NJS_DATE_HR] = 0; tm[NJS_DATE_MIN] = 0; tm[NJS_DATE_SEC] = 0; tm[NJS_DATE_MSEC] = 0; next = njs_date_number_parse(&tm[NJS_DATE_YR], p, end, 4); if (next != NULL) { utc = 1; /* ISO-8601 format: "1970-09-28T06:00:00.000Z" */ if (next == end) { goto done; } if (*next != '-') { /* Extended ISO-8601 format: "+001970-09-28T06:00:00.000Z" */ next = njs_date_number_parse(&ext, next, end, 2); if (njs_slow_path(next == NULL)) { return NAN; } tm[NJS_DATE_YR] *= 100; tm[NJS_DATE_YR] += ext; if (string.start[0] == '-') { if (tm[NJS_DATE_YR] == 0) { return NAN; } tm[NJS_DATE_YR] = -tm[NJS_DATE_YR]; } if (next == end) { goto done; } if (*next != '-') { return NAN; } } p = njs_date_number_parse(&tm[NJS_DATE_MON], next + 1, end, 2); if (njs_slow_path(p == NULL)) { return NAN; } tm[NJS_DATE_MON]--; if (p == end) { goto done; } if (njs_slow_path(*p != '-')) { return NAN; } p = njs_date_number_parse(&tm[NJS_DATE_DAY], p + 1, end, 2); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } if (njs_slow_path(*p != 'T')) { return NAN; } end--; if (*end != 'Z') { utc = 0; end++; } p = njs_date_time_parse(tm, p + 1, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } if (njs_slow_path(p > end || *p != '.')) { return NAN; } p++; for (ms_length = 0; p + ms_length < end; ms_length++) { if (p[ms_length] < '0' || p[ms_length] > '9') { break; } } if (njs_slow_path(njs_date_number_parse(&tm[NJS_DATE_MSEC], p, end, njs_min(ms_length, 3)) == NULL)) { return NAN; } if (ms_length == 1) { tm[NJS_DATE_MSEC] *= 100; } else if (ms_length == 2) { tm[NJS_DATE_MSEC] *= 10; } p += ms_length; if (p < end) { utc_off = njs_date_utc_offset_parse(p, end); if (njs_slow_path(utc_off == -1)) { return NAN; } utc = 1; tm[NJS_DATE_MSEC] += -utc_off * 60000; } done: return njs_make_date(tm, !utc); } if (sign) { return NAN; } week = 1; for ( ;; ) { next = njs_date_number_parse(&tm[NJS_DATE_DAY], 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". */ return njs_date_rfc2822_string_parse(tm, next, end); } tm[NJS_DATE_MON] = njs_date_month_parse(p, end); if (tm[NJS_DATE_MON] >= 0) { /* Date.toString() format: "Mon Sep 28 1970 12:00:00 GMT+0600". */ return njs_date_js_string_parse(tm, p + 3, end); } if (!week) { return NAN; } p = njs_date_skip_week_day(p, end); if (njs_slow_path(p == NULL)) { return NAN; } p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } week = 0; } } static double njs_date_rfc2822_string_parse(int64_t tm[], const u_char *p, const u_char *end) { int64_t gmtoff; p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } tm[NJS_DATE_MON] = njs_date_month_parse(p, end); if (njs_slow_path(tm[NJS_DATE_MON] < 0)) { return NAN; } p = njs_date_skip_spaces(p + 3, end); if (njs_slow_path(p == NULL)) { return NAN; } p = njs_date_number_parse(&tm[NJS_DATE_YR], p, end, 4); if (njs_slow_path(p == NULL)) { return NAN; } gmtoff = 0; if (p == end) { goto done; } p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } p = njs_date_time_parse(tm, p, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } if (njs_slow_path(p + 2 >= end)) { return NAN; } 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 (njs_slow_path(gmtoff == -1)) { return NAN; } } done: tm[NJS_DATE_MSEC] = -gmtoff * 60000; return njs_make_date(tm, 0); } static double njs_date_js_string_parse(int64_t tm[], const u_char *p, const u_char *end) { int64_t gmtoff; p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } p = njs_date_number_parse(&tm[NJS_DATE_DAY], p, end, 2); if (njs_slow_path(p == NULL)) { return NAN; } p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } p = njs_date_number_parse(&tm[NJS_DATE_YR], p, end, 4); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } p = njs_date_time_parse(tm, p, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } if (p + 2 < end && p[0] == 'G' && p[1] == 'M' && p[2] == 'T') { gmtoff = njs_date_gmtoff_parse(&p[3], end); if (njs_fast_path(gmtoff != -1)) { tm[NJS_DATE_MSEC] = -gmtoff * 60000; return njs_make_date(tm, 0); } } return NAN; done: return njs_make_date(tm, 0); } 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 p; } return NULL; } static njs_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 const u_char * njs_date_time_parse(int64_t tm[], const u_char *p, const u_char *end) { p = njs_date_number_parse(&tm[NJS_DATE_HR], p, end, 2); if (njs_slow_path(p == NULL)) { return p; } if (njs_slow_path(p >= end || *p != ':')) { return NULL; } p = njs_date_number_parse(&tm[NJS_DATE_MIN], p + 1, end, 2); if (njs_slow_path(p == NULL)) { return p; } if (p == end) { return p; } if (njs_slow_path(*p != ':')) { return NULL; } return njs_date_number_parse(&tm[NJS_DATE_SEC], p + 1, end, 2); } static int64_t njs_date_gmtoff_parse(const u_char *start, const u_char *end) { int64_t gmtoff, hour, min; const u_char *p; if (njs_fast_path(start + 4 < end && (*start == '+' || *start == '-'))) { p = njs_date_number_parse(&hour, start + 1, end, 2); if (njs_slow_path(p == NULL)) { return -1; } p = njs_date_number_parse(&min, p, end, 2); if (njs_slow_path(p == NULL)) { return -1; } gmtoff = hour * 60 + min; if (*start == '-') { gmtoff = -gmtoff; } return gmtoff; } return -1; } static const u_char * njs_date_number_parse(int64_t *value, const u_char *p, const u_char *end, size_t size) { u_char c; njs_int_t n; n = 0; do { if (njs_slow_path(p >= end)) { return NULL; } c = *p++; /* Values below '0' become >= 208. */ c = c - '0'; if (njs_slow_path(c > 9)) { return NULL; } n = n * 10 + c; size--; } while (size != 0); *value = n; return p; } static const njs_object_prop_init_t njs_date_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(7), NJS_DECLARE_PROP_NAME("Date"), NJS_DECLARE_PROP_HANDLER(STRING_prototype, njs_object_prototype_create, 0, 0), NJS_DECLARE_PROP_NATIVE(STRING_UTC, njs_date_utc, 7, 0), NJS_DECLARE_PROP_NATIVE(STRING_now, njs_date_now, 0, 0), NJS_DECLARE_PROP_NATIVE(STRING_parse, njs_date_parse, 1, 0), }; const njs_object_init_t njs_date_constructor_init = { njs_date_constructor_properties, njs_nitems(njs_date_constructor_properties), }; static njs_int_t njs_date_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(args[0].type)); return NJS_ERROR; } njs_set_number(retval, njs_date(&args[0])->time); return NJS_OK; } static njs_int_t njs_date_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t fmt, njs_value_t *retval) { double time; if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(args[0].type)); return NJS_ERROR; } time = njs_date(&args[0])->time; if (fmt == NJS_DATE_FMT_TO_ISO_STRING && isnan(time)) { njs_range_error(vm, "Invalid time value"); return NJS_ERROR; } return njs_date_string(vm, retval, fmt, time); } static njs_int_t njs_date_string(njs_vm_t *vm, njs_value_t *retval, njs_date_fmt_t fmt, double time) { int year, tz; u_char *p, sign; u_char buf[NJS_DATE_TIME_LEN]; int64_t tm[NJS_DATE_MAX_FIELDS]; 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" }; if (njs_slow_path(isnan(time))) { njs_atom_to_value(vm, retval, NJS_ATOM_STRING_Invalid_Date); return NJS_OK; } p = buf; switch (fmt) { case NJS_DATE_FMT_TO_ISO_STRING: case NJS_DATE_FMT_TO_UTC_STRING: njs_destruct_date(time, tm, 0, 0); year = tm[NJS_DATE_YR]; if (fmt == NJS_DATE_FMT_TO_UTC_STRING) { p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "%s, %02L %s %04d %02L:%02L:%02L GMT", week[tm[NJS_DATE_WDAY]], tm[NJS_DATE_DAY], month[tm[NJS_DATE_MON]], year, tm[NJS_DATE_HR], tm[NJS_DATE_MIN], tm[NJS_DATE_SEC]); break; } if (year >= 0 && year <= 9999) { p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "%04d", year); } else { if (year > 0) { *p++ = '+'; } p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "%06d", year); } p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "-%02L-%02LT%02L:%02L:%02L.%03LZ", tm[NJS_DATE_MON] + 1, tm[NJS_DATE_DAY], tm[NJS_DATE_HR], tm[NJS_DATE_MIN], tm[NJS_DATE_SEC], tm[NJS_DATE_MSEC]); break; case NJS_DATE_FMT_TO_TIME_STRING: case NJS_DATE_FMT_TO_DATE_STRING: case NJS_DATE_FMT_TO_STRING: default: njs_destruct_date(time, tm, 0, 1); if (fmt != NJS_DATE_FMT_TO_TIME_STRING) { p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "%s %s %02L %04L", week[tm[NJS_DATE_WDAY]], month[tm[NJS_DATE_MON]], tm[NJS_DATE_DAY], tm[NJS_DATE_YR]); } if (fmt != NJS_DATE_FMT_TO_DATE_STRING) { tz = -njs_tz_offset(time); sign = (tz < 0) ? '-' : '+'; if (tz < 0) { tz = -tz; } if (p != buf) { *p++ = ' '; } p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "%02L:%02L:%02L GMT%c%02d%02d", tm[NJS_DATE_HR], tm[NJS_DATE_MIN], tm[NJS_DATE_SEC], sign, tz / 60, tz % 60); } } return njs_string_new(vm, retval, buf, p - buf, p - buf); } njs_int_t njs_date_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *date) { if (njs_slow_path(!njs_is_date(date))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(date->type)); return NJS_ERROR; } return njs_date_string(vm, retval, NJS_DATE_FMT_TO_ISO_STRING, njs_date(date)->time); } static njs_int_t njs_date_prototype_get_field(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { double value; int64_t tm[NJS_DATE_MAX_FIELDS]; if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(args[0].type)); return NJS_ERROR; } value = njs_date(&args[0])->time; if (njs_fast_path(!isnan(value))) { value = njs_destruct_date(value, tm, magic & 0xf, magic & 0x40); } njs_set_number(retval, value); return NJS_OK; } static njs_int_t njs_date_prototype_get_timezone_offset(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double value; if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(args[0].type)); return NJS_ERROR; } value = njs_date(&args[0])->time; if (njs_fast_path(!isnan(value))) { value = njs_tz_offset(value); } njs_set_number(retval, value); return NJS_OK; } static njs_int_t njs_date_prototype_set_time(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double time; njs_int_t ret; if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(args[0].type)); return NJS_ERROR; } if (nargs > 1) { if (njs_slow_path(!njs_is_number(&args[1]))) { ret = njs_value_to_numeric(vm, &args[1], &args[1]); if (njs_slow_path(ret != NJS_OK)) { return ret; } } time = njs_timeclip(njs_number(&args[1])); } else { time = NAN; } njs_date(&args[0])->time = time; njs_set_number(retval, time); return NJS_OK; } static njs_int_t njs_date_prototype_to_primitive(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_uint_t hint; if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(args[0].type)); return NJS_ERROR; } if (njs_slow_path(nargs <= 1)) { goto error; } if (njs_slow_path(!njs_is_string(&args[1]))) { ret = njs_value_to_string(vm, &args[1], &args[1]); if (njs_slow_path(ret != NJS_OK)) { return ret; } } if (args[1].atom_id == NJS_ATOM_STRING_unknown) { ret = njs_atom_atomize_key(vm, &args[1]); if (njs_slow_path(ret != NJS_OK)) { return ret; } } switch (args[1].atom_id) { case NJS_ATOM_STRING_number: hint = NJS_HINT_NUMBER; break; case NJS_ATOM_STRING_string: case NJS_ATOM_STRING_default: hint = NJS_HINT_STRING; break; default: error: njs_type_error(vm, "invalid hint"); return NJS_ERROR; } ret = njs_value_to_primitive(vm, &args[0], &args[0], hint | NJS_HINT_FORCE_ORDINARY); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_value_assign(retval, &args[0]); return NJS_OK; } static njs_int_t njs_date_prototype_set_fields(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { double time, num; njs_int_t ret; njs_uint_t since, left, i; int64_t tm[NJS_DATE_MAX_FIELDS]; if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(args[0].type)); return NJS_ERROR; } time = njs_date(&args[0])->time; since = magic & 7; if (njs_slow_path(nargs < 2 || (since != NJS_DATE_YR && isnan(time)))) { time = NAN; goto done; } i = 1; left = njs_min(((magic >> 3) & 7), nargs - 1); njs_destruct_date(time, tm, 0, magic & 0x40); do { ret = njs_value_to_number(vm, &args[i++], &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!isfinite(num)) { tm[NJS_DATE_YR] = INT64_MIN; continue; } tm[since++] = njs_number_to_integer(num); } while (--left); time = njs_make_date(tm, 1); done: njs_date(&args[0])->time = time; njs_set_number(retval, time); return NJS_OK; } static njs_int_t njs_date_prototype_to_json(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t value; njs_flathsh_query_t fhq; if (njs_is_object(njs_argument(args, 0))) { fhq.proto = &njs_object_hash_proto; fhq.key_hash = NJS_ATOM_STRING_toISOString; ret = njs_object_property(vm, njs_object(njs_argument(args, 0)), &fhq, &value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_is_function(&value)) { return njs_function_apply(vm, njs_function(&value), args, nargs, retval); } } njs_type_error(vm, "\"this\" argument is not an object"); return NJS_ERROR; } static const njs_object_prop_init_t njs_date_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER(STRING___proto__, njs_primitive_prototype_get_proto, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER(STRING_constructor, njs_object_prototype_create_constructor, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_NATIVE(STRING_valueOf, njs_date_prototype_value_of, 0, 0), NJS_DECLARE_PROP_NATIVE(STRING_toString, njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_STRING), NJS_DECLARE_PROP_NATIVE(STRING_toDateString, njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_DATE_STRING), NJS_DECLARE_PROP_NATIVE(STRING_toTimeString, njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_TIME_STRING), NJS_DECLARE_PROP_NATIVE(STRING_toLocaleString, njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_STRING), NJS_DECLARE_PROP_NATIVE(STRING_toLocaleDateString, njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_DATE_STRING), NJS_DECLARE_PROP_NATIVE(STRING_toLocaleTimeString, njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_TIME_STRING), NJS_DECLARE_PROP_NATIVE(STRING_toUTCString, njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_UTC_STRING), NJS_DECLARE_PROP_NATIVE(STRING_toISOString, njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_ISO_STRING), NJS_DECLARE_PROP_NATIVE(STRING_toJSON, njs_date_prototype_to_json, 1, 0), NJS_DECLARE_PROP_NATIVE(STRING_getTime, njs_date_prototype_value_of, 0, 0), NJS_DECLARE_PROP_NATIVE(STRING_getFullYear, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_YR, 1)), NJS_DECLARE_PROP_NATIVE(STRING_getUTCFullYear, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_YR, 0)), NJS_DECLARE_PROP_NATIVE(STRING_getMonth, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_MON, 1)), NJS_DECLARE_PROP_NATIVE(STRING_getUTCMonth, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_MON, 0)), NJS_DECLARE_PROP_NATIVE(STRING_getDate, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_DAY, 1)), NJS_DECLARE_PROP_NATIVE(STRING_getUTCDate, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_DAY, 0)), NJS_DECLARE_PROP_NATIVE(STRING_getDay, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_WDAY, 1)), NJS_DECLARE_PROP_NATIVE(STRING_getUTCDay, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_WDAY, 0)), NJS_DECLARE_PROP_NATIVE(STRING_getHours, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_HR, 1)), NJS_DECLARE_PROP_NATIVE(STRING_getUTCHours, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_HR, 0)), NJS_DECLARE_PROP_NATIVE(STRING_getMinutes, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_MIN, 1)), NJS_DECLARE_PROP_NATIVE(STRING_getUTCMinutes, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_MIN, 0)), NJS_DECLARE_PROP_NATIVE(STRING_getSeconds, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_SEC, 1)), NJS_DECLARE_PROP_NATIVE(STRING_getUTCSeconds, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_SEC, 0)), NJS_DECLARE_PROP_NATIVE(STRING_getMilliseconds, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_MSEC, 1)), NJS_DECLARE_PROP_NATIVE(STRING_getUTCMilliseconds, njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_MSEC, 0)), NJS_DECLARE_PROP_NATIVE(STRING_getTimezoneOffset, njs_date_prototype_get_timezone_offset, 0, 0), NJS_DECLARE_PROP_NATIVE(STRING_setTime, njs_date_prototype_set_time, 1, 0), NJS_DECLARE_PROP_NATIVE(STRING_setMilliseconds, njs_date_prototype_set_fields, 1, njs_date_magic2(NJS_DATE_MSEC, 1, 1)), NJS_DECLARE_PROP_NATIVE(STRING_setUTCMilliseconds, njs_date_prototype_set_fields, 1, njs_date_magic2(NJS_DATE_MSEC, 1, 0)), NJS_DECLARE_PROP_NATIVE(STRING_setSeconds, njs_date_prototype_set_fields, 2, njs_date_magic2(NJS_DATE_SEC, 2, 1)), NJS_DECLARE_PROP_NATIVE(STRING_setUTCSeconds, njs_date_prototype_set_fields, 2, njs_date_magic2(NJS_DATE_SEC, 2, 0)), NJS_DECLARE_PROP_NATIVE(STRING_setMinutes, njs_date_prototype_set_fields, 3, njs_date_magic2(NJS_DATE_MIN, 3, 1)), NJS_DECLARE_PROP_NATIVE(STRING_setUTCMinutes, njs_date_prototype_set_fields, 3, njs_date_magic2(NJS_DATE_MIN, 3, 0)), NJS_DECLARE_PROP_NATIVE(STRING_setHours, njs_date_prototype_set_fields, 4, njs_date_magic2(NJS_DATE_HR, 4, 1)), NJS_DECLARE_PROP_NATIVE(STRING_setUTCHours, njs_date_prototype_set_fields, 4, njs_date_magic2(NJS_DATE_HR, 4, 0)), NJS_DECLARE_PROP_NATIVE(STRING_setDate, njs_date_prototype_set_fields, 1, njs_date_magic2(NJS_DATE_DAY, 1, 1)), NJS_DECLARE_PROP_NATIVE(STRING_setUTCDate, njs_date_prototype_set_fields, 1, njs_date_magic2(NJS_DATE_DAY, 1, 0)), NJS_DECLARE_PROP_NATIVE(STRING_setMonth, njs_date_prototype_set_fields, 2, njs_date_magic2(NJS_DATE_MON, 2, 1)), NJS_DECLARE_PROP_NATIVE(STRING_setUTCMonth, njs_date_prototype_set_fields, 2, njs_date_magic2(NJS_DATE_MON, 2, 0)), NJS_DECLARE_PROP_NATIVE(STRING_setFullYear, njs_date_prototype_set_fields, 3, njs_date_magic2(NJS_DATE_YR, 3, 1)), NJS_DECLARE_PROP_NATIVE(STRING_setUTCFullYear, njs_date_prototype_set_fields, 3, njs_date_magic2(NJS_DATE_YR, 3, 0)), /* NJS_DECLARE_PROP_NATIVE, but not writable */ NJS_DECLARE_PROP_VALUE(SYMBOL_toPrimitive, njs_native_function2(njs_date_prototype_to_primitive, 1, 0), NJS_OBJECT_PROP_VALUE_C), }; const njs_object_init_t njs_date_prototype_init = { njs_date_prototype_properties, njs_nitems(njs_date_prototype_properties), }; const njs_object_type_init_t njs_date_type_init = { .constructor = njs_native_ctor(njs_date_constructor, 7, 0), .constructor_props = &njs_date_constructor_init, .prototype_props = &njs_date_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, };