Repository: sinonjs/fake-timers (js)

Issue:
Dates created before useFakeTimers aren't seen as instanceof Date anymore
-   FakeTimers version : @sinonjs/fake-timers@13.0.1
-   Environment : nodejs
-   Example URL : <!-- If you have one, such as a jsbin -->
-   Other libraries you are using: node-opcua, sinon@19.0.0

**What did you expect to happen?**

Date object that have been created before fakeTimer takes actions should still be instanceof Date, as it used to be in previous versions


**What actually happens**


**How to reproduce**

npm install sinon@19

```javascript
 import sinon from "sinon";


 // defined before useFakeTimers
 const minDate = new Date(Date.UTC(1601, 0, 1, 0, 0, 0));
 console.log("minDate instanceof Date (before)", minDate instanceof Date);
 const clock = sinon.useFakeTimers();
 const d = new Date();
 console.log("instanceof Date", d instanceof Date);

 console.log("minDate instanceof Date (after) : should be true but is : ", minDate instanceof Date);

 clock.restore();
```

```javascript
// without using sinon
import FakeTimers from "@sinonjs/fake-timers";

// emulating what sinonjs is doing:
function createClock(config, globalCtx) {
    let FakeTimersCtx = FakeTimers;
    if (globalCtx !== null && typeof globalCtx === "object") {
        FakeTimersCtx = FakeTimers.withGlobal(globalCtx);
    }
    const clock = FakeTimersCtx.install(config);
    clock.restore = clock.uninstall;
    return clock;
}

 // defined before useFakeTimers
 const minDate = new Date(Date.UTC(1601, 0, 1, 0, 0, 0));
 console.log("minDate instanceof Date (before)", minDate instanceof Date);
 
var clock = createClock();
 const d = new Date();
 console.log("instanceof Date", d instanceof Date);

 console.log("minDate instanceof Date (after) : should be true but is : ", minDate instanceof Date);

 clock.restore();

```



Pull request description:
fix #504: make instances of original Date pass as instances of the fake Date
#### Purpose (TL;DR) - mandatory

Fix issue #504 by utilizing the well known symbol `Symbol.hasInstance`
to customise the default behavior of the `instanceof` operator.

Refs
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/hasInstance
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof

<!--
#### Background (Problem in detail)  - optional
-->
<!--
> When relevant, give a more thorough description of what the problem the PR is trying to solve. Examples of good topics for this section are:
> * Link to an existing GitHub issue describing the problem
> * Describing the problem in greater detail than the TL;DR section above
> * How you discovered the issue if it's not already described as an issue on GitHub
> * Discussion of different approaches to solving this problem and why you chose your proposed solution
-->

<!--
#### Solution  - optional
-->
<!--
> When contributing code (and not just fixing typos, documentation and configuration), please describe why/how your solution works. This helps reviewers spot any mistakes in the implementation.
>
> Example:
> "This solution works by adding a `paintBlue()` method"
> Then your reviewer might spot a mistake in the implementation, if `paintBlue()` uses the colour red.
-->


Patch:
diff --git a/src/fake-timers-src.js b/src/fake-timers-src.js
index b2f7e8d..308daf1 100644
--- a/src/fake-timers-src.js
+++ b/src/fake-timers-src.js
@@ -23,6 +23,7 @@ if (typeof require === "function" && typeof module === "object") {
 
 /**
  * Queues a function to be called during a browser's idle periods
+ *
  * @callback RequestIdleCallback
  * @param {function(IdleDeadline)} callback
  * @param {{timeout: number}} options - an options object
@@ -105,6 +106,7 @@ if (typeof require === "function" && typeof module === "object") {
 
 /**
  * Configuration object for the `install` method.
+ *
  * @typedef {object} Config
  * @property {number|Date} [now] a number (in milliseconds) or a Date object (default epoch)
  * @property {string[]} [toFake] names of the methods that should be faked.
@@ -118,6 +120,7 @@ if (typeof require === "function" && typeof module === "object") {
 /* eslint-disable jsdoc/require-property-description */
 /**
  * The internal structure to describe a scheduled fake timer
+ *
  * @typedef {object} Timer
  * @property {Function} func
  * @property {*[]} args
@@ -131,6 +134,7 @@ if (typeof require === "function" && typeof module === "object") {
 
 /**
  * A Node timer
+ *
  * @typedef {object} NodeImmediate
  * @property {function(): boolean} hasRef
  * @property {function(): NodeImmediate} ref
@@ -142,6 +146,7 @@ if (typeof require === "function" && typeof module === "object") {
 
 /**
  * Mocks available features in the specified global namespace.
+ *
  * @param {*} _global Namespace to mock (e.g. `window`)
  * @returns {FakeTimers}
  */
@@ -271,6 +276,7 @@ function withGlobal(_global) {
      * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into
      * number of milliseconds. This is used to support human-readable strings passed
      * to clock.tick()
+     *
      * @param {string} str
      * @returns {number}
      */
@@ -306,6 +312,7 @@ function withGlobal(_global) {
 
     /**
      * Get the decimal part of the millisecond value as nanoseconds
+     *
      * @param {number} msFloat the number of milliseconds
      * @returns {number} an integer number of nanoseconds in the range [0,1e6)
      *
@@ -322,6 +329,7 @@ function withGlobal(_global) {
 
     /**
      * Used to grok the `now` parameter to createClock.
+     *
      * @param {Date|number} epoch the system time
      * @returns {number}
      */
@@ -440,6 +448,10 @@ function withGlobal(_global) {
                     super(...arguments);
                 }
             }
+
+            static [Symbol.hasInstance](instance) {
+                return instance instanceof NativeDate;
+            }
         }
 
         ClockDate.isFake = true;
@@ -464,6 +476,7 @@ function withGlobal(_global) {
         /**
          * A normal Class constructor cannot be called without `new`, but Date can, so we need
          * to wrap it in a Proxy in order to ensure this functionality of Date is kept intact
+         *
          * @type {ClockDate}
          */
         const ClockDateProxy = new Proxy(ClockDate, {
@@ -490,6 +503,7 @@ function withGlobal(_global) {
      * Most of the properties are the original native ones,
      * but we need to take control of those that have a
      * dependency on the current clock.
+     *
      * @returns {object} the partly fake Intl implementation
      */
     function createIntl() {
@@ -662,6 +676,7 @@ function withGlobal(_global) {
     /* eslint consistent-return: "off" */
     /**
      * Timer comparitor
+     *
      * @param {Timer} a
      * @param {Timer} b
      * @returns {number}
@@ -793,6 +808,7 @@ function withGlobal(_global) {
 
     /**
      * Gets clear handler name for a given timer type
+     *
      * @param {string} ttype
      */
     function getClearHandler(ttype) {
@@ -804,6 +820,7 @@ function withGlobal(_global) {
 
     /**
      * Gets schedule handler name for a given timer type
+     *
      * @param {string} ttype
      */
     function getScheduleHandler(ttype) {
@@ -1159,11 +1176,13 @@ function withGlobal(_global) {
 
         /**
          * A high resolution timestamp in milliseconds.
+         *
          * @typedef {number} DOMHighResTimeStamp
          */
 
         /**
          * performance.now()
+         *
          * @returns {DOMHighResTimeStamp}
          */
         function fakePerformanceNow() {


Test patch:
diff --git a/test/fake-timers-test.js b/test/fake-timers-test.js
index 16019b2..7a67b9e 100644
--- a/test/fake-timers-test.js
+++ b/test/fake-timers-test.js
@@ -3369,6 +3369,11 @@ describe("FakeTimers", function () {
         it("mirrors toString output", function () {
             assert.same(this.clock.Date.toString(), Date.toString());
         });
+
+        it("recognises instances of the original Date as instances of itself", function () {
+            var originalDateInstance = new Date();
+            assert(originalDateInstance instanceof this.clock.Date);
+        });
     });
 
     describe("stubTimers", function () {
@@ -4765,6 +4770,7 @@ describe("FakeTimers", function () {
 
         /**
          * Returns elements that are present in both lists.
+         *
          * @function
          * @template E
          * @param {E[]} [list1]
@@ -4777,6 +4783,7 @@ describe("FakeTimers", function () {
 
         /**
          * Get property names and original values from timers module.
+         *
          * @function
          * @param {string[]} [toFake]
          * @returns {{propertyName: string, originalValue: any}[]}
@@ -5923,6 +5930,7 @@ describe("Node Timer: ref(), unref(),hasRef()", function () {
 describe("Intl API", function () {
     /**
      * Tester function to check if the globally hijacked Intl object is plugging into the faked Clock
+     *
      * @param {string} ianaTimeZone - IANA time zone name
      * @param {number} timestamp - UNIX timestamp
      * @returns {boolean}