zod icon indicating copy to clipboard operation
zod copied to clipboard

v4: Allow HH:MM format in `z.iso.datetime()` and `z.iso.time()`

Open timorthi opened this issue 7 months ago • 1 comments

Ports https://github.com/colinhacks/zod/pull/4315 to v4

Closes #4468

Summary by CodeRabbit

  • Documentation

    • Clarified and expanded documentation for ISO datetime and time string validation, including new examples for accepted formats and precision constraints.
  • Tests

    • Updated and expanded test cases to cover ISO datetime and time strings with and without seconds, including new valid and invalid scenarios.
    • Adjusted test patterns and template literals to align with updated validation logic.
  • Style

    • Reformatted function signatures and parameter declarations for improved readability (no functional changes).

timorthi avatar May 24 '25 07:05 timorthi

Walkthrough

The changes update documentation, regex patterns, and tests to ensure that ISO 8601 datetime and time strings without seconds (e.g., "2020-01-01T00:00" and "23:59") are accepted by relevant validators when no precision is set. Test cases and regex logic were adjusted to reflect this behavior.

Changes

File(s) Change Summary
packages/docs/content/api.mdx Updated documentation and examples to clarify acceptance of ISO datetime/time strings without seconds.
packages/zod/src/v4/classic/tests/json-schema.test.ts,
.../template-literal.test.ts
Updated test snapshots and regex patterns to make seconds optional in ISO datetime/time patterns.
packages/zod/src/v4/classic/tests/string.test.ts Switched to z.iso for date/time parsing, added tests for truncated datetime/time formats, updated failures.
packages/zod/src/v4/core/regexes.ts Refactored regex construction for time patterns to make seconds optional when no precision is set.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant HTMLInput
  participant ZodValidator

  User->>HTMLInput: Enter "2025-05-21T12:00" (no seconds)
  HTMLInput->>ZodValidator: Submit value
  ZodValidator->>ZodValidator: Apply regex (seconds optional)
  alt Valid ISO 8601 local datetime
    ZodValidator-->>User: Accept input
  else Invalid format
    ZodValidator-->>User: Reject input
  end

Assessment against linked issues

Objective Addressed Explanation
Accept ISO 8601 local datetime strings without seconds in z.iso.datetime({ local: true }) (#4468)

Possibly related PRs

  • colinhacks/zod#4315: Updates documentation and examples to clarify acceptance of ISO datetime/time strings without seconds, similar to these changes.

Poem

No seconds? No stress—just hours and minutes,
Now Zod's regex lets your input fit in it!
Docs and tests in harmony,
Parsing time more flexibly,
So "2025-05-21T12:00" shines—
Cheers to code that reads between the lines! ⏰

[!NOTE]

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback. Learn more here.


📜 Recent review details

Configuration used: CodeRabbit UI Review profile: ASSERTIVE Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 592de8de6e9c1f1d41aaa68e2c8ba57adced6507 and 38fb8c7140cf7d61a57f27cb0074d9c51dc25162.

📒 Files selected for processing (5)
  • packages/docs/content/api.mdx (4 hunks)
  • packages/zod/src/v4/classic/tests/json-schema.test.ts (4 hunks)
  • packages/zod/src/v4/classic/tests/string.test.ts (4 hunks)
  • packages/zod/src/v4/classic/tests/template-literal.test.ts (1 hunks)
  • packages/zod/src/v4/core/regexes.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/zod/src/v4/classic/tests/string.test.ts (1)
packages/zod/src/v4/core/regexes.ts (3)
  • datetime (101-113)
  • date (82-82)
  • time (96-98)
🔇 Additional comments (26)
packages/zod/src/v4/core/regexes.ts (3)

84-94: Nice refactor on the time regex logic! 🎯

Love how you've separated the seconds regex construction and made the quantifier conditional. The logic is clean and intuitive - when precision is specified, seconds are required (+), otherwise they're optional (?). This perfectly enables the HH:MM format support while keeping existing behavior intact.

The step-by-step construction makes it much easier to understand what's happening compared to the previous inline approach.


96-98: Clean formatting update.

Nice touch making the function signature more readable with the multi-line parameter formatting.


115-118: Consistent formatting improvement.

Good call on the multi-line parameter object - makes it way more readable, especially when parameters have longer names.

packages/zod/src/v4/classic/tests/template-literal.test.ts (1)

554-555: Snapshot update looks spot on! ✅

The updated regex pattern correctly reflects the optional seconds behavior - (:[0-5]\\d(\\.\\d+)?)? nicely captures the "seconds are optional but if present, fractional seconds are also optional" logic.

packages/zod/src/v4/classic/tests/json-schema.test.ts (4)

65-65: JSON Schema snapshot updated correctly! 🎉

The pattern now properly includes the optional seconds group while maintaining support for fractional seconds when they're present. Consistent with the core regex changes.


81-81: Time format snapshot also updated correctly.

Nice consistency - the time format now supports HH:MM just like the datetime format. Same clean optional seconds pattern.


284-284: Consistent datetime pattern across test sections.

Good to see the same pattern update applied consistently in the string formats section too.


301-301: Complete and consistent snapshot updates! 💯

Love seeing all the related snapshots updated consistently. The pattern changes are identical across all occurrences, which shows careful attention to detail in updating all the affected test cases.

packages/docs/content/api.mdx (6)

342-342: Awesome addition: optional seconds in default datetime
This new example clearly shows that z.iso.datetime() accepts "YYYY-MM-DDTHH:MM" when precision isn’t set.


359-359: Great callout: omitted seconds in offset mode
Nice to see "2020-01-01T00:00Z" listed under the offsets example to illustrate seconds can be omitted there too.


366-366: Solid example for local mode
Love how you added the example showing "2020-01-01T00:00" passes when local: true.


376-376: Precision enforcement example looks good
This makes it clear that missing seconds are invalid when a precision option is set.


397-397: z.iso.time doc updated nicely
The description now accurately indicates support for HH:MM or HH:MM:SS[.s+].


402-402: Nice demonstration for time parsing
Good to include time.parse("00:00") to show the HH:MM format is valid.

packages/zod/src/v4/classic/tests/string.test.ts (12)

717-717: LGTM! Clean migration to the new v4 API.

Nice and clean update from the old z.string().datetime() to the new z.iso.datetime() namespace. This aligns perfectly with the v4 migration goals.


723-723: Good addition for testing HH:MM format support.

This test case properly validates that datetime strings without seconds (like "2022-10-13T09:52Z") are now accepted, which is exactly what this PR aims to achieve.


729-729: Smart edge case testing!

This failure test correctly ensures that malformed datetime strings like "2020-10-14T17:42.123+00:00" (where fractional notation is incorrectly applied to minutes) are properly rejected. Good defensive testing.


731-731: Consistent API migration and logical test case.

The migration to z.iso.datetime({ precision: 0 }) is spot on, and adding the HH:MM test case makes total sense here since precision: 0 means no fractional seconds are expected anyway.

Also applies to: 734-734


740-740: Correct behavior for precision requirements.

Good API migration and the failure test makes perfect sense - when precision: 3 is specified, you need exactly 3 fractional second digits, so HH:MM format (which has no seconds at all) should definitely fail.

Also applies to: 747-747


749-749: Nice coverage for offset scenarios.

Clean API migration and the new test case "2020-10-14T17:42+0315" nicely demonstrates that HH:MM format works properly with timezone offsets. Good comprehensive testing.

Also applies to: 757-757


762-762: Perfect combination of features tested.

The API migration is clean and the test case "2020-10-14T17:42+0000" perfectly demonstrates the combination of HH:MM format with timezone offset and no fractional seconds. All the pieces fit together nicely.

Also applies to: 767-767


775-775: Consistent precision logic maintained.

Good API migration and the failure test is spot on - even with offset support, when precision: 4 is specified, HH:MM format should fail since it lacks the required fractional seconds. Logical consistency maintained.

Also applies to: 783-783


787-787: Clean date schema migration.

Straightforward and correct migration from z.string().date() to z.iso.date(). Nice and consistent with the other API updates.


841-841: Core feature implemented correctly!

Perfect API migration and the test case "23:59" is exactly what this PR is all about - allowing HH:MM time format without seconds. This is the key functionality being added.

Also applies to: 849-849


862-862: Good validation boundary testing.

Smart test case with "24:60" - even though HH:MM format is now allowed, invalid hour/minute values should still be properly rejected. Good defensive testing.


864-864: Consistent precision logic for time schemas.

Clean API migration and the failure test correctly enforces that HH:MM format should fail when precision: 2 is specified, since fractional seconds are required. The logic is consistent with datetime precision handling.

Also applies to: 875-875

✨ Finishing Touches
  • [ ] 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

coderabbitai[bot] avatar May 24 '25 07:05 coderabbitai[bot]

Thanks for the PR. I've been pondering this and merged some changes in https://github.com/colinhacks/zod/pull/4680 including a fix for this. Landed in [email protected].

colinhacks avatar Jun 12 '25 07:06 colinhacks