You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make == / != valueOf-aware so DateTime values just work
Equal/notEqual in the core compare two non-null objects by their
`valueOf()` result whenever both operands return a primitive distinct
from themselves. This mirrors what `<` / `>` / `<=` / `>=` already
did via JavaScript's `ToPrimitive` algorithm, and lets values like
Luxon `DateTime` and JS `Date` compare by instant without the core
ever knowing about either type.
Twelve-line change to packages/expreszo/src/operators/binary/comparison.ts:
* `equal(a, b)` short-circuits on `a === b`, then for two non-null
object operands calls `a.valueOf()` and `b.valueOf()`. If neither
result is the object itself (i.e. both have a meaningful primitive
form), it returns `av === bv`. Otherwise falls through to false,
preserving today's reference-equality semantics for plain objects
and arrays.
* `notEqual(a, b)` is now `!equal(a, b)`.
What this changes:
* Two `Date` instances at the same millisecond are now `==`.
* Two Luxon `DateTime` values at the same instant are now `==` (when
the @pro-fa/expreszo-datetime plugin is registered, since that's
the source of the values).
* Boxed primitives (new Number(5), new String('x')) compare by their
primitive form too.
What this does NOT change:
* Plain objects ({a:1} == {a:1}) still false — they have no
overridden valueOf, so the new branch falls through.
* Arrays ([1,2] == [1,2]) still false, same reason.
* Primitives keep their existing semantics.
* null/undefined handling unchanged.
* Object compared to a primitive is still false — no implicit
cross-type coercion.
No plugin changes required. Users register `dateTimePlugin` exactly
as before; all six comparison operators (`<`, `>`, `<=`, `>=`, `==`,
`!=`) work on DateTimes (and JS Dates) automatically.
Tests:
* 9 new tests in packages/expreszo/test/operators/operators-comparison.ts
covering Date↔Date, custom-valueOf objects, Date↔custom-valueOf
cross-type, plain objects (still false), arrays (still false),
NaN-valued Dates (still not equal), object vs primitive,
null handling.
* 4 new tests in packages/expreszo-datetime/test/plugin.test.ts
covering the actual Luxon DateTime path end-to-end and DateTime↔Date
cross-type equality.
Versions:
* @pro-fa/expreszo: 0.6.0 -> 0.6.1
* @pro-fa/expreszo-datetime: 0.2.0 -> 0.2.1, peer-dep range bumped
to ^0.6.1 to require the new equality behaviour.
Docs:
* BREAKING_CHANGES.md gains a 0.6.1 section explaining the change,
what's affected, and what isn't.
* CHANGELOG.md (datetime) gains a 0.2.1 entry pointing at the core
change as the source.
* docs/datetime.md "Comparison" section now leads with the operator
examples and demotes the named functions to the millisecond-truncated
/ range / sortable cases.
* docs/datetime-integration.md gains an "Operators on DateTime values"
section explaining the valueOf duck-typing and noting that
arithmetic operators (+, -) are intentionally not covered.
Verified: yarn workspaces run lint/type-check/test all green
(2200 core + 25 datetime + 12 mcp = 2237 tests). mkdocs build
--strict clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/datetime-integration.md
+16Lines changed: 16 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -54,6 +54,22 @@ Functions that **produce** a date return a Luxon `DateTime` so chains stay effic
54
54
55
55
The full normaliser lives at [`packages/expreszo-datetime/src/normalize.ts`](https://github.com/Pro-Fa/expreszo-typescript/blob/main/packages/expreszo-datetime/src/normalize.ts) and is exported as `toDateTime` / `toDateTimeOrUndefined` for callers who want to do their own conversions outside an expression.
56
56
57
+
## Operators on DateTime values
58
+
59
+
As of `@pro-fa/expreszo` 0.6.1, the core comparison operators are `valueOf`-aware: any two non-null objects whose `.valueOf()` returns a primitive distinct from themselves compare by that primitive. So with `dateTimePlugin` registered (or just a JS `Date` in your `variables` map):
No operator overrides, no separate plugin — the relational operators (`<`, `>`, `<=`, `>=`) always worked because JS's `ToPrimitive` already calls `valueOf`; `==` and `!=` now follow suit.
68
+
69
+
Plain objects and arrays still use reference equality, so the change is targeted at value-bearing objects (`Date`, Luxon `DateTime`, boxed primitives) only.
70
+
71
+
Arithmetic operators (`+`, `-`) are intentionally **not** DateTime-aware — `addDuration`, `subtractDuration`, and `diff` are explicit about units and avoid the "what does `date + 7` mean" footgun.
72
+
57
73
## Wiring the plugin into the language service
58
74
59
75
`createLanguageService` accepts the same plugins via a `plugins` option, so completions, hover, and diagnostics see the datetime functions too:
Copy file name to clipboardExpand all lines: packages/expreszo-datetime/CHANGELOG.md
+8Lines changed: 8 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,6 +4,14 @@ All notable changes to `@pro-fa/expreszo-datetime` are documented here.
4
4
5
5
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
7
+
## 0.2.1
8
+
9
+
### Improved
10
+
11
+
`==` and `!=` now compare two `DateTime` values by instant. This isn't a change in this package — it's a side-effect of `@pro-fa/expreszo` 0.6.1 making the core operators `valueOf`-aware. With `dateTimePlugin` registered, **all six comparison operators** (`<`, `>`, `<=`, `>=`, `==`, `!=`) work as expected on DateTimes (and on JS `Date` values from your variables map). No additional wiring needed.
12
+
13
+
The peer-dependency range now requires `@pro-fa/expreszo` ≥ 0.6.1.
Copy file name to clipboardExpand all lines: packages/expreszo/BREAKING_CHANGES.md
+20Lines changed: 20 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,26 @@
2
2
3
3
This document lists breaking changes in the library to help users migrate between versions.
4
4
5
+
## 0.6.1 — `==` / `!=` are now valueOf-aware
6
+
7
+
`equal` and `notEqual` (the `==` and `!=` operators) now compare two non-null objects by their `valueOf()` result whenever both operands return a primitive distinct from themselves. This mirrors what `<` / `>` / `<=` / `>=` already did via JavaScript's `ToPrimitive` algorithm, and lets values like Luxon `DateTime` and JS `Date` compare by instant without the core knowing about either type.
8
+
9
+
**What changed:**
10
+
11
+
- Two `Date` instances at the same millisecond are now `==`. They were `!=` before (reference equality on objects).
12
+
- Two Luxon `DateTime` values at the same instant are now `==` (when the `@pro-fa/expreszo-datetime` plugin is registered).
13
+
- Boxed primitives — `new Number(5)`, `new String('x')` — also start comparing by their primitive form.
14
+
15
+
**What did not change:**
16
+
17
+
- Plain objects (`{a:1} == {a:1}`) still return `false` — they have no overridden `valueOf()`, so the new branch falls through to reference equality.
18
+
- Arrays (`[1,2] == [1,2]`) still return `false`, same reason.
19
+
- All primitive comparisons (`3 == 3`, `'a' == 'a'`) keep their existing semantics.
20
+
-`null` continues to equal only `null`; `undefined` continues to equal only `undefined`.
21
+
- Object compared to a primitive is still `false` — no implicit cross-type coercion.
22
+
23
+
This is technically a behaviour change for code that was relying on `Date == Date` returning `false`. Audit any expression that explicitly compared two date-like objects expecting reference inequality.
24
+
5
25
## 0.6.0 — companion packages and `parser.use(plugin)`
0 commit comments