[njs] Refactored working with Date object.

Dmitry Volyntsev xeioex at nginx.com
Fri Nov 8 13:30:17 UTC 2019


details:   https://hg.nginx.org/njs/rev/f770bdea7c85
branches:  
changeset: 1226:f770bdea7c85
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Fri Nov 08 16:29:29 2019 +0300
description:
Refactored working with Date object.

1) Replacing gmtime_r() and localtime_r() with builtin function.
2) Custom getters and setters for each field (like hours, minutes,
   seconds) are replaced with generic functions.

diffstat:

 auto/time                |    16 +
 src/njs_date.c           |  1730 +++++++++------------------------------------
 src/test/njs_unit_test.c |    49 +-
 3 files changed, 408 insertions(+), 1387 deletions(-)

diffs (truncated from 2507 to 1000 lines):

diff -r 6df48738a043 -r f770bdea7c85 auto/time
--- a/auto/time	Fri Nov 08 16:29:24 2019 +0300
+++ b/auto/time	Fri Nov 08 16:29:29 2019 +0300
@@ -33,6 +33,22 @@ if [ $njs_found = no ]; then
     fi
 fi
 
+
+njs_feature="sizeof(time_t)"
+njs_feature_name=NJS_TIME_T_SIZE
+njs_feature_run=value
+njs_feature_incs=
+njs_feature_libs=
+njs_feature_test="#include <time.h>
+                  #include <stdio.h>
+
+                  int main(void) {
+                      printf(\"%zu\", sizeof(time_t));
+                      return 0;
+                  }"
+. auto/feature
+
+
 # Linux, FreeBSD, MacOSX.
 
 njs_feature="struct tm.tm_gmtoff"
diff -r 6df48738a043 -r f770bdea7c85 src/njs_date.c
--- a/src/njs_date.c	Fri Nov 08 16:29:24 2019 +0300
+++ b/src/njs_date.c	Fri Nov 08 16:29:29 2019 +0300
@@ -8,15 +8,27 @@
 #include <njs_main.h>
 
 
-/*
- * njs_timegm() is used because
- *   Solaris lacks timegm(),
- *   FreeBSD and MacOSX timegm() cannot handle years before 1900.
- */
-
 #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
+
+
+#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,
@@ -28,23 +40,20 @@ typedef enum {
 
 
 static double njs_date_string_parse(njs_value_t *date);
-static double njs_date_rfc2822_string_parse(struct tm *tm, const u_char *p,
+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(struct tm *tm, const u_char *p,
+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(struct tm *tm, const u_char *p,
+static const u_char *njs_date_time_parse(int64_t tm[], const u_char *p,
     const u_char *end);
-static njs_int_t njs_date_gmtoff_parse(const u_char *start, const u_char *end);
-static const u_char *njs_date_number_parse(int *value, const u_char *p,
+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 int64_t njs_timegm(struct tm *tm);
 static njs_int_t njs_date_string(njs_vm_t *vm, njs_value_t *retval,
     njs_date_fmt_t fmt, double time);
-static double njs_date_time(struct tm *tm, int64_t ms);
-static double njs_date_utc_time(struct tm *tm, double time);
 
 
 static const njs_value_t  njs_string_invalid_date = njs_string("Invalid Date");
@@ -95,13 +104,6 @@ njs_timeclip(double time)
 
 
 njs_inline int64_t
-njs_make_time(int64_t h, int64_t min, int64_t s, int64_t milli)
-{
-    return ((h * 60 + min) * 60 + s) * 1000 + milli;
-}
-
-
-njs_inline int64_t
 njs_days_in_year(int64_t y)
 {
     return 365 + !(y % 4) - !(y % 100) + !(y % 400);
@@ -152,25 +154,151 @@ njs_tz_offset(int64_t time)
 
     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_make_date(int64_t days, int64_t time, njs_bool_t local)
+njs_year_from_days(int64_t *days)
 {
-    int64_t  date;
+    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--;
+            d1 += njs_days_in_year(y);
+
+        } else {
+            nd = njs_days_in_year(y);
+
+            if (d1 < nd) {
+                break;
+            }
 
-    date = days * 86400000 + time;
+            d1 -= nd;
+            y++;
+        }
+    }
+
+    *days = d1;
+
+    return y;
+}
+
+
+njs_inline double
+njs_make_date(int64_t tm[], njs_bool_t local)
+{
+    int64_t  days, time;
+
+    days = njs_make_day(tm[NJS_DATE_YR], tm[NJS_DATE_MON],
+                        tm[NJS_DATE_DAY]);
+
+    time = ((tm[NJS_DATE_HR] * 60 + tm[NJS_DATE_MIN]) * 60
+            + tm[NJS_DATE_SEC]) * 1000 + tm[NJS_DATE_MSEC];
+
+    time += days * 86400000;
 
     if (local) {
-        date += njs_tz_offset(date) * 60000;
+        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;
     }
 
-    return date;
+    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];
 }
 
 
@@ -179,11 +307,10 @@ njs_date_constructor(njs_vm_t *vm, njs_v
     njs_index_t unused)
 {
     double      num, time;
-    int64_t     day, tm;
     njs_int_t   ret;
     njs_uint_t  i, n;
     njs_date_t  *date;
-    int64_t     values[8];
+    int64_t     tm[NJS_DATE_MAX_FIELDS];
 
     if (!vm->top_frame->ctor) {
         return njs_date_string(vm, &vm->retval, NJS_DATE_FMT_TO_STRING,
@@ -210,17 +337,16 @@ njs_date_constructor(njs_vm_t *vm, njs_v
             time = njs_date_string_parse(&args[1]);
 
         } else {
-            time = njs_number(&args[1]);
+            time = njs_timeclip(njs_number(&args[1]));
         }
 
     } else {
 
         time = NAN;
 
-        njs_memzero(values, 8 * sizeof(int64_t));
+        njs_memzero(tm, NJS_DATE_MAX_FIELDS * sizeof(int64_t));
 
-        /* Day. */
-        values[3] = 1;
+        tm[NJS_DATE_DAY] = 1;
 
         n = njs_min(8, nargs);
 
@@ -238,19 +364,14 @@ njs_date_constructor(njs_vm_t *vm, njs_v
                 goto done;
             }
 
-            values[i] = num;
+            tm[i] = num;
         }
 
-        /* Year. */
-        if (values[1] >= 0 && values[1] < 100) {
-            values[1] += 1900;
+        if (tm[NJS_DATE_YR] >= 0 && tm[NJS_DATE_YR] < 100) {
+            tm[NJS_DATE_YR] += 1900;
         }
 
-        day = njs_make_day(values[1], values[2], values[3]);
-
-        tm = njs_make_time(values[4], values[5], values[6], values[7]);
-
-        time = njs_make_date(day, tm, 1);
+        time = njs_make_date(tm, 1);
     }
 
 done:
@@ -268,7 +389,7 @@ done:
     date->object.extensible = 1;
     date->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_DATE].object;
 
-    date->time = njs_timeclip(time);
+    date->time = time;
 
     njs_set_date(&vm->retval, date);
 
@@ -280,19 +401,17 @@ static njs_int_t
 njs_date_utc(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
-    int64_t     day, tm;
     double      num, time;
     njs_int_t   ret;
     njs_uint_t  i, n;
-    int64_t     values[8];
+    int64_t     tm[NJS_DATE_MAX_FIELDS];
 
     time = NAN;
 
     if (nargs > 1) {
-        njs_memzero(values, 8 * sizeof(int64_t));
+        njs_memzero(tm, NJS_DATE_MAX_FIELDS * sizeof(int64_t));
 
-        /* Day. */
-        values[3] = 1;
+        tm[NJS_DATE_DAY] = 1;
 
         n = njs_min(8, nargs);
 
@@ -310,18 +429,15 @@ njs_date_utc(njs_vm_t *vm, njs_value_t *
                 goto done;
             }
 
-            values[i] = num;
+            tm[i] = num;
         }
 
         /* Year. */
-        if (values[1] >= 0 && values[1] < 100) {
-            values[1] += 1900;
+        if (tm[NJS_DATE_YR] >= 0 && tm[NJS_DATE_YR] < 100) {
+            tm[NJS_DATE_YR] += 1900;
         }
 
-        day = njs_make_day(values[1], values[2], values[3]);
-        tm = njs_make_time(values[4], values[5], values[6], values[7]);
-
-        time = njs_timeclip(njs_make_date(day, tm, 0));
+        time = njs_make_date(tm, 0);
     }
 
 done:
@@ -332,55 +448,6 @@ done:
 }
 
 
-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_int_t
 njs_date_now(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
@@ -421,12 +488,12 @@ njs_date_parse(njs_vm_t *vm, njs_value_t
 static double
 njs_date_string_parse(njs_value_t *date)
 {
-    int                ext, ms, ms_length, skipped;
-    double             time;
-    njs_str_t          string;
-    struct tm          tm;
-    njs_bool_t         sign, week, utc;
-    const u_char       *p, *next, *end;
+    size_t         ms_length;
+    int64_t        ext, skipped;
+    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(date, &string);
 
@@ -445,19 +512,20 @@ njs_date_string_parse(njs_value_t *date)
         sign = 0;
     }
 
-    tm.tm_mon = 0;
-    tm.tm_mday = 1;
-    tm.tm_hour = 0;
-    tm.tm_min = 0;
-    tm.tm_sec = 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.tm_year, p, end, 4);
+    next = njs_date_number_parse(&tm[NJS_DATE_YR], p, end, 4);
 
     if (next != NULL) {
         /* ISO-8601 format: "1970-09-28T06:00:00.000Z" */
 
         if (next == end) {
-            goto year;
+            goto done;
         }
 
         if (*next != '-') {
@@ -468,18 +536,19 @@ njs_date_string_parse(njs_value_t *date)
                 return NAN;
             }
 
-            tm.tm_year = tm.tm_year * 100 + ext;
+            tm[NJS_DATE_YR] *= 100;
+            tm[NJS_DATE_YR] += ext;
 
             if (string.start[0] == '-') {
-                if (tm.tm_year == 0) {
+                if (tm[NJS_DATE_YR] == 0) {
                     return NAN;
                 }
 
-                tm.tm_year = -tm.tm_year;
+                tm[NJS_DATE_YR] = -tm[NJS_DATE_YR];
             }
 
             if (next == end) {
-                goto year;
+                goto done;
             }
 
             if (*next != '-') {
@@ -487,14 +556,12 @@ njs_date_string_parse(njs_value_t *date)
             }
         }
 
-        tm.tm_year -= 1900;
-
-        p = njs_date_number_parse(&tm.tm_mon, next + 1, end, 2);
+        p = njs_date_number_parse(&tm[NJS_DATE_MON], next + 1, end, 2);
         if (njs_slow_path(p == NULL)) {
             return NAN;
         }
 
-        tm.tm_mon--;
+        tm[NJS_DATE_MON]--;
 
         if (p == end) {
             goto done;
@@ -504,7 +571,7 @@ njs_date_string_parse(njs_value_t *date)
             return NAN;
         }
 
-        p = njs_date_number_parse(&tm.tm_mday, p + 1, end, 2);
+        p = njs_date_number_parse(&tm[NJS_DATE_DAY], p + 1, end, 2);
         if (njs_slow_path(p == NULL)) {
             return NAN;
         }
@@ -525,7 +592,7 @@ njs_date_string_parse(njs_value_t *date)
            end++;
         }
 
-        p = njs_date_time_parse(&tm, p + 1, end);
+        p = njs_date_time_parse(tm, p + 1, end);
         if (njs_slow_path(p == NULL)) {
             return NAN;
         }
@@ -542,7 +609,7 @@ njs_date_string_parse(njs_value_t *date)
 
         ms_length = (end - p < 3) ? end - p : 3;
 
-        p = njs_date_number_parse(&ms, p, end, ms_length);
+        p = njs_date_number_parse(&tm[NJS_DATE_MSEC], p, end, ms_length);
         if (njs_slow_path(p == NULL)) {
             return NAN;
         }
@@ -555,21 +622,13 @@ njs_date_string_parse(njs_value_t *date)
         }
 
         if (ms_length == 1) {
-            ms *= 100;
+            tm[NJS_DATE_MSEC] *= 100;
 
         } else if (ms_length == 2) {
-            ms *= 10;
+            tm[NJS_DATE_MSEC] *= 10;
         }
 
-        if (utc) {
-            time = njs_timegm(&tm);
-
-        } else {
-            tm.tm_isdst = -1;
-            time = mktime(&tm);
-        }
-
-        return time * 1000 + ms;
+        return njs_make_date(tm, !utc);
     }
 
     if (sign) {
@@ -579,7 +638,7 @@ njs_date_string_parse(njs_value_t *date)
     week = 1;
 
     for ( ;; ) {
-        next = njs_date_number_parse(&tm.tm_mday, p, end, 2);
+        next = njs_date_number_parse(&tm[NJS_DATE_DAY], p, end, 2);
 
         if (next != NULL) {
             /*
@@ -588,15 +647,15 @@ njs_date_string_parse(njs_value_t *date)
              *   "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);
+            return njs_date_rfc2822_string_parse(tm, next, end);
         }
 
-        tm.tm_mon = njs_date_month_parse(p, end);
+        tm[NJS_DATE_MON] = njs_date_month_parse(p, end);
 
-        if (tm.tm_mon >= 0) {
+        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);
+            return njs_date_js_string_parse(tm, p + 3, end);
         }
 
         if (!week) {
@@ -616,28 +675,24 @@ njs_date_string_parse(njs_value_t *date)
         week = 0;
     }
 
-year:
-
-    tm.tm_year -= 1900;
-
 done:
 
-    return njs_timegm(&tm) * 1000;
+    return njs_make_date(tm, 0);
 }
 
 
 static double
-njs_date_rfc2822_string_parse(struct tm *tm, const u_char *p, const u_char *end)
+njs_date_rfc2822_string_parse(int64_t tm[], const u_char *p, const u_char *end)
 {
-    int  gmtoff;
+    int64_t  gmtoff;
 
     p = njs_date_skip_spaces(p, end);
     if (njs_slow_path(p == NULL)) {
         return NAN;
     }
 
-    tm->tm_mon = njs_date_month_parse(p, end);
-    if (njs_slow_path(tm->tm_mon < 0)) {
+    tm[NJS_DATE_MON] = njs_date_month_parse(p, end);
+    if (njs_slow_path(tm[NJS_DATE_MON] < 0)) {
         return NAN;
     }
 
@@ -646,12 +701,12 @@ njs_date_rfc2822_string_parse(struct tm 
         return NAN;
     }
 
-    p = njs_date_number_parse(&tm->tm_year, p, end, 4);
+    p = njs_date_number_parse(&tm[NJS_DATE_YR], p, end, 4);
     if (njs_slow_path(p == NULL)) {
         return NAN;
     }
 
-    tm->tm_year -= 1900;
+    gmtoff = 0;
 
     if (p == end) {
         goto done;
@@ -701,25 +756,25 @@ njs_date_rfc2822_string_parse(struct tm 
         }
     }
 
-    return (njs_timegm(tm) - gmtoff * 60) * 1000;
-
 done:
 
-    return njs_timegm(tm) * 1000;
+    tm[NJS_DATE_MSEC] = -gmtoff * 60000;
+
+    return njs_make_date(tm, 0);
 }
 
 
 static double
-njs_date_js_string_parse(struct tm *tm, const u_char *p, const u_char *end)
+njs_date_js_string_parse(int64_t tm[], const u_char *p, const u_char *end)
 {
-    int  gmtoff;
+    int64_t  gmtoff;
 
     p = njs_date_skip_spaces(p, end);
     if (njs_slow_path(p == NULL)) {
         return NAN;
     }
 
-    p = njs_date_number_parse(&tm->tm_mday, p, end, 2);
+    p = njs_date_number_parse(&tm[NJS_DATE_DAY], p, end, 2);
     if (njs_slow_path(p == NULL)) {
         return NAN;
     }
@@ -729,13 +784,11 @@ njs_date_js_string_parse(struct tm *tm, 
         return NAN;
     }
 
-    p = njs_date_number_parse(&tm->tm_year, p, end, 4);
+    p = njs_date_number_parse(&tm[NJS_DATE_YR], p, end, 4);
     if (njs_slow_path(p == NULL)) {
         return NAN;
     }
 
-    tm->tm_year -= 1900;
-
     if (p == end) {
         goto done;
     }
@@ -768,11 +821,11 @@ njs_date_js_string_parse(struct tm *tm, 
     }
 
     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)) {
-            return (njs_timegm(tm) - gmtoff * 60) * 1000;
+            tm[NJS_DATE_MSEC] = -gmtoff * 60000;
+            return njs_make_date(tm, 0);
         }
     }
 
@@ -780,7 +833,7 @@ njs_date_js_string_parse(struct tm *tm, 
 
 done:
 
-    return njs_timegm(tm) * 1000;
+    return njs_make_date(tm, 0);
 }
 
 
@@ -908,9 +961,9 @@ njs_date_month_parse(const u_char *p, co
 
 
 static const u_char *
-njs_date_time_parse(struct tm *tm, const u_char *p, const u_char *end)
+njs_date_time_parse(int64_t tm[], const u_char *p, const u_char *end)
 {
-    p = njs_date_number_parse(&tm->tm_hour, p, end, 2);
+    p = njs_date_number_parse(&tm[NJS_DATE_HR], p, end, 2);
     if (njs_slow_path(p == NULL)) {
         return p;
     }
@@ -919,7 +972,7 @@ njs_date_time_parse(struct tm *tm, const
         return NULL;
     }
 
-    p = njs_date_number_parse(&tm->tm_min, p + 1, end, 2);
+    p = njs_date_number_parse(&tm[NJS_DATE_MIN], p + 1, end, 2);
     if (njs_slow_path(p == NULL)) {
         return p;
     }
@@ -932,14 +985,14 @@ njs_date_time_parse(struct tm *tm, const
         return NULL;
     }
 
-    return njs_date_number_parse(&tm->tm_sec, p + 1, end, 2);
+    return njs_date_number_parse(&tm[NJS_DATE_SEC], p + 1, end, 2);
 }
 
 
-static njs_int_t
+static int64_t
 njs_date_gmtoff_parse(const u_char *start, const u_char *end)
 {
-    int           gmtoff, hour, min;
+    int64_t       gmtoff, hour, min;
     const u_char  *p;
 
     if (njs_fast_path(start + 4 < end && (*start == '+' || *start == '-'))) {
@@ -968,7 +1021,7 @@ njs_date_gmtoff_parse(const u_char *star
 
 
 static const u_char *
-njs_date_number_parse(int *value, const u_char *p, const u_char *end,
+njs_date_number_parse(int64_t *value, const u_char *p, const u_char *end,
     size_t size)
 {
     u_char     c;
@@ -1128,11 +1181,10 @@ static njs_int_t
 njs_date_string(njs_vm_t *vm, njs_value_t *retval, njs_date_fmt_t fmt,
     double time)
 {
-    u_char     *p, sign;
-    int32_t    year, tz;
-    time_t     clock;
-    u_char     buf[NJS_DATE_TIME_LEN];
-    struct tm  tm;
+    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" };
@@ -1150,15 +1202,15 @@ njs_date_string(njs_vm_t *vm, njs_value_
     switch (fmt) {
     case NJS_DATE_FMT_TO_ISO_STRING:
     case NJS_DATE_FMT_TO_UTC_STRING:
-        clock = time / 1000;
-        gmtime_r(&clock, &tm);
-        year = tm.tm_year + 1900;
+        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, %02d %s %04d %02d:%02d:%02d GMT",
-                            week[tm.tm_wday], tm.tm_mday, month[tm.tm_mon],
-                            year, tm.tm_hour, tm.tm_min, tm.tm_sec);
+                            "%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;
         }
@@ -1175,10 +1227,9 @@ njs_date_string(njs_vm_t *vm, njs_value_
         }
 
         p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN,
-                        "-%02d-%02dT%02d:%02d:%02d.%03dZ",
-                        tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
-                        tm.tm_min, tm.tm_sec,
-                        (int) ((int64_t) time % 1000));
+                        "-%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;
 
@@ -1186,14 +1237,13 @@ njs_date_string(njs_vm_t *vm, njs_value_
     case NJS_DATE_FMT_TO_DATE_STRING:
     case NJS_DATE_FMT_TO_STRING:
     default:
-        clock = time / 1000;
-        localtime_r(&clock, &tm);
+        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 %02d %04d",
-                            week[tm.tm_wday], month[tm.tm_mon], tm.tm_mday,
-                            tm.tm_year + 1900);
+                            "%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) {
@@ -1209,8 +1259,8 @@ njs_date_string(njs_vm_t *vm, njs_value_
             }
 
             p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN,
-                            "%02d:%02d:%02d GMT%c%02d%02d",
-                            tm.tm_hour, tm.tm_min, tm.tm_sec,
+                            "%02L:%02L:%02L GMT%c%02d%02d",
+                            tm[NJS_DATE_HR], tm[NJS_DATE_MIN], tm[NJS_DATE_SEC],
                             sign, tz / 60, tz % 60);
         }
     }
@@ -1259,193 +1309,11 @@ njs_date_to_string(njs_vm_t *vm, njs_val
 
 
 static njs_int_t
-njs_date_prototype_get_full_year(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused)
-{
-    double     value;
-    time_t     clock;
-    struct tm  tm;
-
-    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))) {
-        clock = value / 1000;
-        localtime_r(&clock, &tm);
-
-        value = tm.tm_year + 1900;
-    }
-
-    njs_set_number(&vm->retval, value);
-
-    return NJS_OK;
-}
-
-
-static njs_int_t
-njs_date_prototype_get_utc_full_year(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused)
-{
-    double     value;
-    time_t     clock;
-    struct tm  tm;
-
-    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))) {
-        clock = value / 1000;
-        gmtime_r(&clock, &tm);
-
-        value = tm.tm_year + 1900;
-    }
-
-    njs_set_number(&vm->retval, value);
-
-    return NJS_OK;
-}
-
-
-static njs_int_t
-njs_date_prototype_get_month(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    double     value;
-    time_t     clock;
-    struct tm  tm;
-
-    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))) {
-        clock = value / 1000;
-        localtime_r(&clock, &tm);
-
-        value = tm.tm_mon;
-    }
-
-    njs_set_number(&vm->retval, value);
-
-    return NJS_OK;
-}
-
-
-static njs_int_t
-njs_date_prototype_get_utc_month(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused)
+njs_date_prototype_get_field(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t magic)
 {
-    double     value;
-    time_t     clock;
-    struct tm  tm;
-
-    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))) {
-        clock = value / 1000;
-
-        gmtime_r(&clock, &tm);
-
-        value = tm.tm_mon;
-    }
-
-    njs_set_number(&vm->retval, value);
-
-    return NJS_OK;
-}
-
-
-static njs_int_t
-njs_date_prototype_get_date(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    double     value;
-    time_t     clock;
-    struct tm  tm;
-
-    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))) {
-        clock = value / 1000;
-        localtime_r(&clock, &tm);
-
-        value = tm.tm_mday;
-    }
-


More information about the nginx-devel mailing list