Njs: fix Date.UTC()

Created on 9 Oct 2019  路  2Comments  路  Source: nginx/njs

Here is the patch:
```diff

HG changeset patch

User Artem S. Povalyukhin artem.povaluhin@gmail.com

Date 1570621389 -10800

Wed Oct 09 14:43:09 2019 +0300

Node ID edcb0a9abb0d8a1d7b57f12706e8d2fce82c2f6c

Parent 9f1ba171d81e661e78b9303dc37c699304bf2f03

Fixed Date.UTC().

diff -r 9f1ba171d81e -r edcb0a9abb0d src/njs_date.c
--- a/src/njs_date.c Tue Oct 08 15:56:58 2019 +0300
+++ b/src/njs_date.c Wed Oct 09 14:43:09 2019 +0300
@@ -181,7 +181,7 @@ njs_date_utc(njs_vm_t *vm, njs_value_t *

 time = NAN;
  • if (nargs > 2) {
  • if (nargs > 1) {
    njs_memzero(values, 8 * sizeof(int32_t));

     n = njs_min(8, nargs);
    

    @@ -196,7 +196,7 @@ njs_date_utc(njs_vm_t *vm, njs_value_t *

         num = njs_number(&args[i]);
    
  • if (isnan(num)) {

  • if (isnan(num) || isinf(num)) {
    goto done;
    }

@@ -210,7 +210,7 @@ njs_date_utc(njs_vm_t *vm, njs_value_t *

     tm.tm_year = values[1];
     tm.tm_mon = values[2];

- tm.tm_mday = values[3];
+ tm.tm_mday = (nargs > 3) ? values[3] : 1;
tm.tm_hour = values[4];
tm.tm_min = values[5];
tm.tm_sec = values[6];
diff -r 9f1ba171d81e -r edcb0a9abb0d src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Tue Oct 08 15:56:58 2019 +0300
+++ b/src/test/njs_unit_test.c Wed Oct 09 14:43:09 2019 +0300
@@ -11532,6 +11532,24 @@ static njs_unit_test_t njs_test[] =
{ njs_str("Date.UTC({valueOf:()=>2011}, 5, 24, 6, 0)"),
njs_str("1308895200000") },

  • { njs_str("Date.UTC()"),
  • njs_str("NaN") },
    +
  • { njs_str("Date.UTC(Infinity)"),
  • njs_str("NaN") },
    +
  • { njs_str("Date.UTC(Infinity, 0)"),
  • njs_str("NaN") },
    +
  • { njs_str("Date.UTC(1970)"),
  • njs_str("0") },
    +
  • { njs_str("Date.UTC(1970, 0)"),
  • njs_str("0") },
    +
  • { njs_str("Date.UTC(1970, 0, 0)"),
  • njs_str("-86400000") },
    +
    { njs_str("Date.parse()"),
    njs_str("NaN") },
    ```
bug test262

All 2 comments

@drsm

Looks good, but also

http://www.ecma-international.org/ecma-262/9.0/index.html#sec-date.utc is required

Return TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli))).

njs_timeclip() primitive is already present, but Make* primitives are not.

   built-ins/Date/UTC/non-integer-values in strict mode
   built-ins/Date/UTC/overflow-make-day in strict mode
   built-ins/Date/UTC/time-clip in strict mode
   built-ins/Date/UTC/year-offset in strict mode

@drsm take a look

# HG changeset patch
# User Artem S. Povalyukhin <[email protected]>
# Date 1570621389 -10800
#      Wed Oct 09 14:43:09 2019 +0300
# Node ID 39ccfd66c4581d911c02d4473579d5591734366a
# Parent  fa75ca7ed851843495730e16f2774b55c953385d
Fixed Date.UTC() according to specification.

diff --git a/src/njs_date.c b/src/njs_date.c
--- a/src/njs_date.c
+++ b/src/njs_date.c
@@ -44,7 +44,29 @@ static double njs_date_utc_time(struct t
 static const njs_value_t  njs_string_invalid_date = njs_string("Invalid Date");


-static uint64_t
+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;
@@ -55,7 +77,7 @@ njs_gettime(void)
 }


-static double
+njs_inline double
 njs_timeclip(double time)
 {
     if (isinf(time) || isnan(time) || fabs(time) > 8.64e15) {
@@ -66,6 +88,62 @@ 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);
+}
+
+
+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);
+}
+
+
+static int64_t
+njs_make_day(int64_t yr, int64_t month, int64_t date)
+{
+    int64_t  i, ym, mn, md, days;
+
+    const int month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+    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_make_date(int64_t days, int64_t time)
+{
+    return days * 86400000 + time;
+}
+
+
 njs_int_t
 njs_date_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
@@ -173,16 +251,16 @@ 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;
-    struct tm   tm;
     njs_int_t   ret;
     njs_uint_t  i, n;
-    int32_t     values[8];
+    int64_t     values[8];

     time = NAN;

-    if (nargs > 2) {
-        njs_memzero(values, 8 * sizeof(int32_t));
+    if (nargs > 1) {
+        njs_memzero(values, 8 * sizeof(int64_t));

         n = njs_min(8, nargs);

@@ -196,7 +274,7 @@ njs_date_utc(njs_vm_t *vm, njs_value_t *

             num = njs_number(&args[i]);

-            if (isnan(num)) {
+            if (isnan(num) || isinf(num)) {
                 goto done;
             }

@@ -204,18 +282,19 @@ njs_date_utc(njs_vm_t *vm, njs_value_t *
         }

         /* Year. */
-        if (values[1] > 99) {
-            values[1] -= 1900;
+        if (0 <= values[1] && 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];
+        /* Day. */
+        if (nargs <= 3) {
+            values[3] = 1;
+        }
+
+        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));
     }

 done:
diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c
+++ b/src/test/njs_unit_test.c
@@ -11625,6 +11625,48 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("Date.UTC({valueOf:()=>2011}, 5, 24, 6, 0)"),
       njs_str("1308895200000") },

+    { njs_str("Date.UTC()"),
+      njs_str("NaN") },
+
+    { njs_str("Date.UTC(Infinity)"),
+      njs_str("NaN") },
+
+    { njs_str("Date.UTC(Infinity, 0)"),
+      njs_str("NaN") },
+
+    { njs_str("Date.UTC(1970)"),
+      njs_str("0") },
+
+    { njs_str("Date.UTC(1968, 24)"),
+      njs_str("0") },
+
+    { njs_str("[-1,0,1,99,100].map(yr => Date.UTC(yr))"),
+      njs_str("-62198755200000,-2208988800000,-2177452800000,915148800000,-59011459200000") },
+
+    { njs_str("Date.UTC(1970.9, 0.9, 1.9, 0.9, 0.9, 0.9, 0.9)"),
+      njs_str("0") },
+
+    { njs_str("Date.UTC(-1970.9, -0.9, -0.9, -0.9, -0.9, -0.9, -0.9)"),
+      njs_str("-124334438400000") },
+
+    { njs_str("Date.UTC(275760, 8, 13, 0, 0, 0, 0)"),
+      njs_str("8640000000000000") },
+
+    { njs_str("Date.UTC(275760, 8, 13, 0, 0, 0, 1)"),
+      njs_str("NaN") },
+
+    { njs_str("Date.UTC(-271821, 3, 20, 0, 0, 0, 0)"),
+      njs_str("-8640000000000000") },
+
+    { njs_str("Date.UTC(-271821, 3, 20, 0, 0, 0, -1)"),
+      njs_str("NaN") },
+
+    { njs_str("Date.UTC(1970, 0)"),
+      njs_str("0") },
+
+    { njs_str("Date.UTC(1970, 0, 0)"),
+      njs_str("-86400000") },
+
     { njs_str("Date.parse()"),
       njs_str("NaN") },

Was this page helpful?
0 / 5 - 0 ratings