Skip to content

fix(json-schema): only coerce date strings with a valid time suffix - #2212

Merged
dinwwwh merged 1 commit into
mainfrom
claude/eager-franklin-em8aot
Oct 13, 2026
Merged

dinwwwh merged 1 commit into
mainfrom
claude/eager-franklin-em8aot

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Oct 13, 2026

Copy link
Copy Markdown
Member

JsonSchemaCoercer turns strings into Dates for schemas marked x-native-type: 'date' (z.date(), v.date(), arktype Date). DATE_TIME_PATTERN allowed any text after T or a space. The day-range check only looks at the leading YYYY-MM-DD, so the whole string reached new Date(). V8's legacy parser then read the trailing words and returned a different date, which the validator accepted silently:

Input Before After
'2020-06-15 jan' 2020-01-06 left as a string
'2020-01-15 December' 2020-12-01 left as a string
'2020-06-15 12:00 jan' 2020-01-06T12:00Z left as a string

This breaks the coercer's safe/lossless rule, and it is a gap in the pattern tightening from #2127.

Change

The suffix is now limited to a time (HH:MM, optional seconds and fraction), optionally followed by Z or a ±HH:MM / ±HHMM offset:

const DATE_TIME_PATTERN = /^(?!-0{6})(?:[+-]\d{2})?\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/

The new pattern accepts a strict subset of what the old one did, so it can only coerce fewer strings. The day-range and NaN checks are unchanged, and it is still one regex test per string. Every documented form still coerces: date only, T/space with a time, seconds, fractions, Z, offsets with or without a colon, and expanded years. The only correctly parsed form that now stays a string is a lowercase z ('…T06:15:00z'), which zod's z.iso.datetime() and valibot's isoTimestamp() reject too.

Tests

  • New test: '2020-06-15 jan', '2020-01-15 December', '2020-06-15 12:00 jan', '2020-01-01T06:15:00 GMT' and '2020-01-01Tfoo' are returned unchanged. It fails before the fix.
  • '2020-01-01 06:15:00+07:00' still coerces.

pnpm vitest run for json-schema, zod, valibot, arktype and tests/plugins (266 tests), pnpm type:check and pnpm lint all pass. In the coercer bench, the cases that run the regex were the same or slightly faster.

🤖 Generated with Claude Code

https://claude.ai/code/session_0179UCu7YNr8SzgX4xx6MkrU


Generated by Claude Code

`DATE_TIME_PATTERN` allowed any text after `T` or a space, so V8's legacy
parser read trailing words and produced a different date that validation
accepted (`'2020-06-15 jan'` became 2020-01-06). The suffix is now limited
to a time with optional seconds, fraction and `Z` or numeric offset, so
such strings stay strings and fail validation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0179UCu7YNr8SzgX4xx6MkrU
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
orpc 5c09136 Commit Preview URL

Branch Preview URL
Oct 12 2026, 06:22 AM

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

  • Tightened the date coercion pattern (packages/json-schema/src/coercer.ts:436): the time suffix is now required to be HH:MM[:SS[.fff]][Z|±HH:MM] instead of accepting arbitrary trailing text, so V8's legacy parser can no longer silently reinterpret trailing words as a different date.
  • Regression tests (packages/json-schema/src/coercer.test.ts:129-139): adds a space-separated offset positive case and asserts dates followed by non-time text ('2020-06-15 jan', '2020-01-01Tfoo', etc.) are left untouched.

I confirmed the new pattern is a strict subset of the old one, all existing date cases still coerce, and the only V8-parseable form newly left as a string is a lowercase z — which is not strict ISO 8601 and is rejected by zod's z.iso.datetime() and valibot's isoTimestamp(), matching the documented "ISO 8601 date and datetime strings" contract in docs/plugins/smart-coercion.mdx. packages/json-schema/src/coercer.test.ts passes (67 tests).

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@pkg-pr-new

pkg-pr-new Bot commented Oct 13, 2026

Copy link
Copy Markdown
More templates

@orpc/ai-sdk

npm i https://pkg.pr.new/@orpc/ai-sdk@2212

@orpc/arktype

npm i https://pkg.pr.new/@orpc/arktype@2212

@orpc/bun

npm i https://pkg.pr.new/@orpc/bun@2212

@orpc/client

npm i https://pkg.pr.new/@orpc/client@2212

@orpc/cloudflare

npm i https://pkg.pr.new/@orpc/cloudflare@2212

@orpc/contract

npm i https://pkg.pr.new/@orpc/contract@2212

@orpc/experimental-effect

npm i https://pkg.pr.new/@orpc/experimental-effect@2212

@orpc/evlog

npm i https://pkg.pr.new/@orpc/evlog@2212

@orpc/hibernation

npm i https://pkg.pr.new/@orpc/hibernation@2212

@orpc/json-schema

npm i https://pkg.pr.new/@orpc/json-schema@2212

@orpc/experimental-lock

npm i https://pkg.pr.new/@orpc/experimental-lock@2212

@orpc/experimental-msw

npm i https://pkg.pr.new/@orpc/experimental-msw@2212

@orpc/nest

npm i https://pkg.pr.new/@orpc/nest@2212

@orpc/next

npm i https://pkg.pr.new/@orpc/next@2212

@orpc/node

npm i https://pkg.pr.new/@orpc/node@2212

@orpc/openapi

npm i https://pkg.pr.new/@orpc/openapi@2212

@orpc/opentelemetry

npm i https://pkg.pr.new/@orpc/opentelemetry@2212

@orpc/pinia-colada

npm i https://pkg.pr.new/@orpc/pinia-colada@2212

@orpc/pino

npm i https://pkg.pr.new/@orpc/pino@2212

@orpc/publisher

npm i https://pkg.pr.new/@orpc/publisher@2212

@orpc/ratelimit

npm i https://pkg.pr.new/@orpc/ratelimit@2212

@orpc/server

npm i https://pkg.pr.new/@orpc/server@2212

@orpc/shared

npm i https://pkg.pr.new/@orpc/shared@2212

@orpc/swr

npm i https://pkg.pr.new/@orpc/swr@2212

@orpc/tanstack-query

npm i https://pkg.pr.new/@orpc/tanstack-query@2212

@orpc/trpc

npm i https://pkg.pr.new/@orpc/trpc@2212

@orpc/valibot

npm i https://pkg.pr.new/@orpc/valibot@2212

@orpc/zod

npm i https://pkg.pr.new/@orpc/zod@2212

commit: 5c09136

@codecov

codecov Bot commented Oct 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed

codspeed Bot commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

⚠️ 5 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

✅ 28 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing claude/eager-franklin-em8aot (5c09136) with main (5f441da)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@dinwwwh
dinwwwh merged commit 42c2ba7 into main Oct 13, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants