Skip to content

Commit 05882e1

Browse files
HF-307 PR 3: the release-date comment promised an agreement that does not hold
`releaseDateTimestamp` said it reads HT_RELEASE_DATE "so a perpetual typed key and a legacy key agree on what 'this build' means". They do not, east of UTC. This function uses `Date.UTC`; the legacy validator parses the same env value with `new Date(month/day/year)`, which is LOCAL. Measured at process level: HT_RELEASE_DATE=10/08/2026 legacy (local) typed (UTC) TZ=UTC, TZ=America/Los_Angeles 20675 20675 agree TZ=Asia/Tokyo 20674 20675 differ TZ=Pacific/Kiritimati 20674 20675 differ Raised by Bugbot on 12.08 and left unanswered for four days while I reported the PR as review-clean off the check status - which it was not. The CODE is right and stays. UTC is required for a typed key: key spec rev 5 §1.2 makes offline/online parity a hard rule, and a local clock breaks it. Legacy keeps its local parse because legacy behaviour is frozen this release - switching it would move the expiry verdict of already-issued keys by a day for every customer east of UTC. So the fix is to stop the comment claiming the opposite, and to state the consequence plainly: two customers east of UTC, one on a legacy key and one on an equivalent typed key, can disagree by a day about whether this build is covered. Reconciling them is a product decision. No test accompanies this, deliberately. The property is not observable in this suite: assigning `process.env.TZ` mid-run has no effect once the runtime resolved its timezone (probed - UTC, Asia/Tokyo and Pacific/Kiritimati all returned an identical timestamp inside Jest), and CI runs in UTC where both parses agree. A test written that way passes whichever parse the source uses; I wrote one, mutation-checked it, found it vacuous, and removed it rather than ship an assertion that cannot fail. Pinning it needs a timezone-parameterised CI job. The reasoning sits next to the release-axis tests so the gap stays deliberate. Tests: handsontable/hyperformula-tests#32 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GuUdq242TtFaepKRNdEkj9
1 parent d236367 commit 05882e1

1 file changed

Lines changed: 24 additions & 2 deletions

File tree

src/license/licenseResolution.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,30 @@ interface LicenseTerms {
127127
* The build's release date as epoch milliseconds (UTC midnight), or `null` when it is unknown or
128128
* malformed.
129129
*
130-
* Read from the same `HT_RELEASE_DATE` (`DD/MM/YYYY`) the legacy validator uses, so a perpetual
131-
* typed key and a legacy key agree on what "this build" means.
130+
* Read from the same `HT_RELEASE_DATE` (`DD/MM/YYYY`) the legacy validator uses, but **parsed
131+
* differently on purpose**, and the difference is observable — so do not "simplify" either one to
132+
* match the other without reading this.
133+
*
134+
* This function uses `Date.UTC`. The legacy validator builds the same value with
135+
* `new Date(month/day/year)`, which is parsed in the host's LOCAL zone. East of UTC the two land on
136+
* different day numbers for one and the same release date:
137+
*
138+
* ```text
139+
* HT_RELEASE_DATE=10/08/2026 legacy (local) this function (UTC)
140+
* TZ=UTC, TZ=America/Los_Angeles 20675 20675 agree
141+
* TZ=Asia/Tokyo 20674 20675 differ by a day
142+
* TZ=Pacific/Kiritimati 20674 20675 differ by a day
143+
* ```
144+
*
145+
* UTC is the required reading for a typed key: key spec rev 5 §1.2 makes offline/online parity a
146+
* hard rule — the offline check and a future online check must return the same verdict for the same
147+
* key at the same instant — and any rule reading a local clock breaks it. The legacy path keeps its
148+
* local parse because legacy behaviour is frozen for this release; switching it would move the
149+
* expiry verdict of already-issued legacy keys by a day for every customer east of UTC.
150+
*
151+
* The consequence, flagged rather than hidden: two customers east of UTC, one on a legacy key and
152+
* one on an equivalent typed key, can disagree by a day about whether this build is covered.
153+
* Reconciling them is a product decision, not a refactor.
132154
*/
133155
function releaseDateTimestamp(): number | null {
134156
const [day, month, year] = (process.env.HT_RELEASE_DATE ?? '').split('/')

0 commit comments

Comments
 (0)