JSON-Patch icon indicating copy to clipboard operation
JSON-Patch copied to clipboard

feat: Add native Date object support to fast-json-patch

Open nyamathshaik opened this issue 1 month ago • 0 comments

Add Native Date Object Support

Summary

This PR adds proper handling for JavaScript Date objects in JSON Patch operations. Currently, fast-json-patch converts Date objects to ISO strings during cloning, which causes type loss and incorrect comparisons. This PR fixes that by preserving Date objects throughout the patching process.

Problem

When using compare() on objects containing Date objects:

const obj1 = { startTime: new Date('2025-11-11T10:00:00Z') };
const obj2 = { startTime: new Date('2025-11-11T11:00:00Z') };

const patches = compare(obj1, obj2);
// Currently: Date objects get converted to strings or cause errors
// With this PR: Date objects are preserved and compared correctly

Root Causes:

  1. _deepClone uses JSON.stringify/parse - This converts Date objects to ISO strings
  2. No Date-specific comparison - Date objects are compared by reference (===), not by value

Solution

1. Enhanced _deepClone Function

  • Added special handling for Date objects: new Date(obj.getTime())
  • Changed to recursive cloning instead of JSON serialization
  • Date objects are now cloned as Date instances, not strings

Before:

const clone = JSON.parse(JSON.stringify(dateObj));
// Result: "2025-11-11T10:00:00.000Z" (string)

After:

const clone = new Date(dateObj.getTime());
// Result: Date object with same timestamp

2. Enhanced _generate Function

  • Added Date-specific comparison using getTime()
  • Date objects with different timestamps generate replace patches
  • Date objects with same timestamps generate no patches (optimization)

Before:

if (oldVal !== newVal) // Always true for Date objects (reference comparison)

After:

if (oldVal instanceof Date && newVal instanceof Date) {
  if (oldVal.getTime() !== newVal.getTime()) {
    // Generate patch only if timestamps differ
  }
}

Changes

File Lines Changed Description
src/helpers.ts ~30 Rewrote _deepClone with Date support
src/duplex.ts ~15 Added Date comparison in _generate
test/spec/dateSpec.mjs ~190 New comprehensive test suite

Testing

New Tests (11 specs)

✅ Clone Date objects correctly ✅ Clone objects containing Date objects ✅ Clone arrays with Date objects ✅ Detect Date changes in compare() ✅ No patches for identical Dates ✅ Handle Date objects in nested structures ✅ Detect Date additions ✅ Detect Date removals ✅ Apply Date patches with applyPatch() ✅ Real-world calendar events scenario

Existing Tests

✅ All 215 existing tests pass (core, duplex, validate)

Real-World Use Case

This fix is crucial for workflows that handle:

  • 📅 Calendar events from APIs (Google Calendar, Outlook, etc.)
  • ⏰ Scheduled tasks with timestamps
  • 📊 Time-series data
  • 🕐 Audit logs with creation/modification times

Example: Workflow Context Diffing

// Before: Date objects caused errors or became strings
const context = {
  variables: {
    events: [
      {
        start: new Date('2025-11-15T10:00:00Z'),
        end: new Date('2025-11-15T11:00:00Z')
      }
    ]
  }
};

const patches = compare(oldContext, context);
// Now works correctly with Date objects preserved!

Performance

  • No performance impact for non-Date objects
  • Slightly slower for Date objects (recursive vs JSON serialization)
  • Trade-off is acceptable for correctness

Backward Compatibility

Fully backward compatible

  • All existing tests pass
  • No breaking changes to API
  • Behavior unchanged for non-Date objects

Related Issues

This fixes issues where:

  • Date objects are converted to ISO strings during patching
  • Reference comparison causes false positives for Date changes
  • Patches contain strings instead of Date objects

Checklist

  • [x] Code changes implemented
  • [x] Tests added (11 new specs)
  • [x] All existing tests pass (215 specs)
  • [x] TypeScript types updated
  • [x] Documentation in commit message
  • [ ] CHANGELOG.md updated (maintainer to decide)
  • [ ] Ready for review

Would love feedback on:

  1. Performance trade-offs of recursive cloning vs JSON serialization
  2. Whether to add similar support for other special objects (RegExp, Map, Set, etc.)
  3. API design - should this be opt-in via a flag or default behavior?

nyamathshaik avatar Nov 11 '25 11:11 nyamathshaik