Skip to content

new Date() fails for any date in October on leap years #1386

@HBehrens

Description

@HBehrens

The return value of new Date(2016, 9, day) (or any other leap year) is an invalid date object (console.log(…) --> "Invalid Date") since the binary search in ecma_date_make_day() is erroneous.
We do have a working fix, covered by a brute-force unit-test that creates dates in the range from 1950...2050 (that previously failed for every day that follow the aforementioned pattern), but due to time constraints, I won't be able to put up a PR before mid next week.

If for some reason somebody needs a fix before, here's are possible fix:

@@ -781,13 +781,16 @@ ecma_date_make_day (ecma_number_t year, /**< year value */
       delta = delta / 2;
     }

-    if (ecma_date_month_from_time (time + step) < mn)
+    time += step * ECMA_DATE_MS_PER_DAY;
+
+    /* Make sure time represents a day in the previous month */
+    if (ecma_date_month_from_time (time) >= mn)
     {
-      time += step * ECMA_DATE_MS_PER_DAY;
+      time -= ECMA_DATE_MS_PER_DAY * 31;
     }

-    /* Get the month's first day */
-    while (ecma_date_month_from_time (time) < mn)
+    /* Get the month's first day, don't check for <= as the month wraps */
+    while (ecma_date_month_from_time (time) != mn)
     {
       time += ECMA_DATE_MS_PER_DAY;
     }

Also, these are the tests, we verified this fix against:

+void test_rocky_api_util__ecma_date_make_day(void) {
+  cl_assert_equal_d(16861, ecma_date_make_day(2016, 2, 1));  // JerryScript's unit-test
+  cl_assert_equal_d(-25294, ecma_date_make_day(1900, 9, 1));  // not a leap year!
+  cl_assert_equal_d(17075, ecma_date_make_day(2016, 8, 31)); // Sept-31 == Oct-01
+  cl_assert_equal_d(17075, ecma_date_make_day(2016, 9, 1));  // Oct-01
+  cl_assert_equal_d(17045, ecma_date_make_day(2016, 8, 1));  // Sept-01
+}
+
+void test_rocky_api_util__ecma_date_make_day_list(void) {
+  int fail_count = 0;
+  for(int y = 1950; y < 2050; y++) {
+    for(int m = 0; m < 12; m++) {
+      for (int d = 1; d < 32; d++) {
+        const ecma_number_t result = ecma_date_make_day(y, m, d);
+        if (isnan(result)) {
+          printf("failed for %04d-%02d-%02d\n", y, m, d);
+          fail_count++;
+        } else {
+//          printf("passed for %04d-%02d-%02d: %d\n", y, m, d, (int)result);
+        }
+      }
+    }
+  }
+  cl_assert_equal_i(0, fail_count);
+}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugUndesired behaviourecma builtinsRelated to ECMA built-in routines

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions