Repository: prestojs/prestojs (language: python)

Issue:
DateTimeField/DateField isEqual
Current these fields don't do equality checks on `Date` objects. We probably should:

```
    isEqual(a?: Date, b?: Date): boolean {
        if (!a || !b) {
            return false;
        }
        return a === b || a.getTime() === b.getTime();
    }
```


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f5a43145..c4dd2738 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,8 @@
 * [DecimalWidget](https://prestojs.com/docs/ui-antd/widgets/DecimalWidget) now stores values as a string to allow high precision.
 * [IntegerWidget](https://prestojs.com/docs/ui-antd/widgets/IntegerWidget) now forces entered value to be an integer (ie. decimal places are removed)
 * [NumberField](https://prestojs.com/docs/viewmodel/fields/NumberField) now accepts either string or number for `minValue` & `maxValue` to support strings that may come from `DecimalField`.
+* [DateField](https://prestojs.com/docs/viewmodel/fields/DateField) & [DateTimeField](https://prestojs.com/docs/viewmodel/fields/DateTimeField) now consider dates that represent the same value to be equal
+  * Without this change if a record with one of these fields was to be updated via `viewModelCachingMiddleware` and it was identical it would still always replace the existing record.
 
 ## [0.0.30] - 2022-04-13
 
diff --git a/js-packages/@prestojs/viewmodel/src/fields/DateField.ts b/js-packages/@prestojs/viewmodel/src/fields/DateField.ts
index 956d6a48..6028ff15 100644
--- a/js-packages/@prestojs/viewmodel/src/fields/DateField.ts
+++ b/js-packages/@prestojs/viewmodel/src/fields/DateField.ts
@@ -21,6 +21,21 @@ export default class DateField extends Field<Date, string | Date> {
         return new Date(value);
     }
 
+    isEqual(a?: Date, b?: Date): boolean {
+        if (a === b) {
+            return true;
+        }
+        if (!a || !b) {
+            return false;
+        }
+        return (
+            // We only compare date components as there's technically no time component for DateField
+            a.getFullYear() === b.getFullYear() &&
+            a.getMonth() === b.getMonth() &&
+            a.getDate() === b.getDate()
+        );
+    }
+
     normalize(value: string | Date): Date | null {
         if (value instanceof Date) {
             return value;
diff --git a/js-packages/@prestojs/viewmodel/src/fields/DateTimeField.ts b/js-packages/@prestojs/viewmodel/src/fields/DateTimeField.ts
index a96320a3..70f6427e 100644
--- a/js-packages/@prestojs/viewmodel/src/fields/DateTimeField.ts
+++ b/js-packages/@prestojs/viewmodel/src/fields/DateTimeField.ts
@@ -18,6 +18,15 @@ export default class DateTimeField extends Field<Date, string | Date> {
         }
         return new Date(value);
     }
+    isEqual(a?: Date, b?: Date): boolean {
+        if (a === b) {
+            return true;
+        }
+        if (!a || !b) {
+            return false;
+        }
+        return a.getTime() === b.getTime();
+    }
     normalize(value: string | Date): Date | null {
         if (value instanceof Date) {
             return value;


Test patch:
diff --git a/js-packages/@prestojs/viewmodel/src/fields/__tests__/DateField.test.ts b/js-packages/@prestojs/viewmodel/src/fields/__tests__/DateField.test.ts
index 7b15dbae..ca0ea3be 100644
--- a/js-packages/@prestojs/viewmodel/src/fields/__tests__/DateField.test.ts
+++ b/js-packages/@prestojs/viewmodel/src/fields/__tests__/DateField.test.ts
@@ -11,3 +11,22 @@ test('DateField parse values correctly', () => {
     const now = new Date();
     expect((field.parse(now) as Date).toISOString()).toBe(now.toISOString());
 });
+
+test('should consider distinct date instances with same date as equal', () => {
+    const field = new DateField({ label: 'date' });
+    const a = field.parse('2019-11-25') as Date;
+    const b = field.parse('2019-11-25') as Date;
+    const c = field.parse('2019-11-26') as Date;
+    const d = field.parse('2018-11-25') as Date;
+    const e = field.parse('2019-10-25') as Date;
+    // time component ignored
+    const f = field.parse('2019-11-25 10:00:58 GMT+0') as Date;
+    expect(a).toBeTruthy();
+    expect(b).toBeTruthy();
+    expect(field.isEqual(a, b)).toBe(true);
+    expect(field.isEqual(a, c)).toBe(false);
+    expect(field.isEqual(a, d)).toBe(false);
+    expect(field.isEqual(a, e)).toBe(false);
+    expect(field.isEqual(a, f)).toBe(true);
+    expect(field.isEqual(undefined, undefined)).toBe(true);
+});
diff --git a/js-packages/@prestojs/viewmodel/src/fields/__tests__/DateTimeField.test.ts b/js-packages/@prestojs/viewmodel/src/fields/__tests__/DateTimeField.test.ts
index c4344722..221e9fee 100644
--- a/js-packages/@prestojs/viewmodel/src/fields/__tests__/DateTimeField.test.ts
+++ b/js-packages/@prestojs/viewmodel/src/fields/__tests__/DateTimeField.test.ts
@@ -13,3 +13,20 @@ test('DateTimeField parse values correctly', () => {
     const now = new Date();
     expect((field.parse(now) as Date).toISOString()).toBe(now.toISOString());
 });
+test('should consider distinct datetime instances with same date + time as equal', () => {
+    const field = new DateTimeField({ label: 'date' });
+    const a = field.parse('2019-11-25 23:59:59 GMT+0') as Date;
+    const b = field.parse('2019-11-25 23:59:59 GMT+0') as Date;
+    const c = field.parse('2019-11-26 23:59:59 GMT+0') as Date;
+    const d = field.parse('2018-11-25 23:59:59 GMT+0') as Date;
+    const e = field.parse('2019-10-25 23:59:59 GMT+0') as Date;
+    const f = field.parse('2019-11-25 23:59:58 GMT+0') as Date;
+    expect(a).toBeTruthy();
+    expect(b).toBeTruthy();
+    expect(field.isEqual(a, b)).toBe(true);
+    expect(field.isEqual(a, c)).toBe(false);
+    expect(field.isEqual(a, d)).toBe(false);
+    expect(field.isEqual(a, e)).toBe(false);
+    expect(field.isEqual(a, f)).toBe(false);
+    expect(field.isEqual(undefined, undefined)).toBe(true);
+});