Skip to content

Add rfc3339Duration validation action - #1520

Open
NoahStarkenburg wants to merge 5 commits into
open-circle:mainfrom
NoahStarkenburg:feat-iso-duration
Open

Add rfc3339Duration validation action#1520
NoahStarkenburg wants to merge 5 commits into
open-circle:mainfrom
NoahStarkenburg:feat-iso-duration

Conversation

@NoahStarkenburg

@NoahStarkenburg NoahStarkenburg commented Jun 27, 2026

Copy link
Copy Markdown

Closes #1497

Adds an isoDuration action that validates ISO 8601 / RFC 3339 duration strings, for OpenAPI format: duration compatibility.

Valid: P1D, P1W, PT5H30M, P1Y2M3DT4H5M6S. Rejects empty P/PT, fractional values, out-of-order units, the mixed week form, and lowercase units.

It follows the existing iso* action pattern (like isoTimeSecond): a new regex in regex.ts, the action, runtime tests, and type tests. The regex is taken from ajv-formats, the duration profile that JSON Schema and OpenAPI tooling expect. Happy to adjust the name or strictness if you prefer.

Summary by CodeRabbit

  • New Features

    • Added RFC 3339 duration validation support to the library.
    • Exposed the new duration validator through the main actions entry point.
    • Added a reusable regular expression for RFC 3339 duration string checks.
  • Tests

    • Added coverage for valid and invalid duration formats.
    • Added type-level checks for the new validator’s inputs, outputs, and issue shape.

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement New feature or request labels Jun 27, 2026
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

A new rfc3339Duration validation action is added to the library. A RFC_3339_DURATION_REGEX constant is added to library/src/regex.ts. The action module at library/src/actions/rfc3339Duration/rfc3339Duration.ts defines Rfc3339DurationIssue, Rfc3339DurationAction, and the rfc3339Duration factory function with two overloads. The factory returns a synchronous action that tests string values against the regex and calls _addIssue on failure. The module is re-exported via its sub-index and the top-level actions barrel. Runtime tests and TypeScript declaration tests are included.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR adds a built-in RFC 3339 duration validator, covers the documented valid examples, and rejects invalid forms as requested in #1497.
Out of Scope Changes check ✅ Passed The changes are all directly tied to the new duration validator, including exports, regex, implementation, and tests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title clearly matches the main change: adding a new rfc3339Duration validation action.

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

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
library/src/actions/isoDuration/isoDuration.test.ts (1)

121-123: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: add P1WT1H to the mixed-week rejection cases.

The regex routes weeks through a week-only alternative, so combining W with a time component (e.g., P1WT1H) is also rejected. Adding it here would document that boundary alongside the existing P1W2D/P1Y1W cases.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@library/src/actions/isoDuration/isoDuration.test.ts` around lines 121 - 123,
The mixed-week rejection test in isoDuration.test.ts only covers calendar/mixed
date cases, but the week-only parsing path in the isoDuration action also
rejects week durations combined with time components. Update the `for mixed
week` case in `action`/`expectActionIssue` to include `P1WT1H` alongside the
existing invalid inputs so the test documents this boundary in
`isoDuration.test.ts`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@library/src/actions/isoDuration/isoDuration.test.ts`:
- Around line 121-123: The mixed-week rejection test in isoDuration.test.ts only
covers calendar/mixed date cases, but the week-only parsing path in the
isoDuration action also rejects week durations combined with time components.
Update the `for mixed week` case in `action`/`expectActionIssue` to include
`P1WT1H` alongside the existing invalid inputs so the test documents this
boundary in `isoDuration.test.ts`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8e705011-b2c6-4937-9fa2-a7720114077e

📥 Commits

Reviewing files that changed from the base of the PR and between 1f9b183 and 98ba3aa.

📒 Files selected for processing (6)
  • library/src/actions/index.ts
  • library/src/actions/isoDuration/index.ts
  • library/src/actions/isoDuration/isoDuration.test-d.ts
  • library/src/actions/isoDuration/isoDuration.test.ts
  • library/src/actions/isoDuration/isoDuration.ts
  • library/src/regex.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 6 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread library/src/actions/isoDuration/isoDuration.ts Outdated
@fabian-hiller fabian-hiller self-assigned this Jun 28, 2026
@fabian-hiller fabian-hiller added this to the v1.5 milestone Jun 28, 2026
});

test('for fractional values', () => {
expectActionIssue(action, baseIssue, ['P0.5Y', 'PT0.5S', 'PT1.5H']);

@yslpn yslpn Jun 28, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch, thanks. I updated the regex to accept a decimal fraction with a full stop or a comma on a component, so P0.5Y, PT1.5H and P0,5Y are now valid and moved into the passing cases.

@yslpn

yslpn commented Jun 28, 2026

Copy link
Copy Markdown
Member

Several important things:

Don't try to invent regular expressions yourself. Don't use AI for generation. You need to use something proven.

It's probably impossible to choose a regular expression that covers all the specification's cases. In that case, you need to choose the most practical one and explain why it's better. And describe the limitations in the documentation. By the way, the method's name will also depend on this. You need to leave room for future expansion, should anyone need it. rfc3339Duration? xmlSchemaDuration?

For example, there's W3C XML Schema xs:duration

https://www.w3.org/TR/xmlschema11-2/#regexs

const duration =
/^-?P((([0-9]+Y([0-9]+M)?([0-9]+D)?|([0-9]+M)([0-9]+D)?|([0-9]+D))(T(([0-9]+H)([0-9]+M)?([0-9]+(\.[0-9]+)?S)?|([0-9]+M)([0-9]+(\.[0-9 ]+)?S)?|([0-9]+(\.[0-9]+)?S)))?)|(T(([0-9]+H)([0-9]+M)?([0-9]+(\.[0-9]+)?S)?|([0-9]+M)([0-9]+(\.[0-9]+)?S)?|([0-9]+(\.[0-9]+)?S))))$/;

Key differences from the OpenAPI/RFC3339 profile: W3C XSD allows negative durations like -P1D and fractional seconds like PT1.5S, but does not support the week-only P2W form. The W3C form is based on PnYnMnDTnHnMnS, not the full ISO 8601 duration zoo.

There's also a library with 100 million downloads per month.
https://www.npmjs.com/package/ajv-formats
They use this regular expression: https://github.com/ajv-validator/ajv-formats/blob/master/src/formats.ts#L54

const duration = /^P(?!$)((\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?|(\d+W)?)$/

Its purpose: it's not a full ISO 8601 duration, but a fairly narrow and practical profile, close to what's expected in JSON Schema/OpenAPI tools. It supports weeks, but not negative weeks, and it doesn't support fractional values ​​separated by periods or commas. Probably for our case this is not necessary and I shouldn’t have left that comment above.

In short, there are a lot of thoughts. A lot of decisions need to be made. What do you think about this? Would you like to see your vision?

Validates ISO 8601 duration strings such as P1Y2M3DT4H5M6S, PT5H30M and
P1W, for OpenAPI format: duration compatibility. Uses the duration regex
from ajv-formats, which covers the practical profile used by JSON Schema
and OpenAPI tooling. Negative and fractional values are not accepted.
Follows the existing iso* action pattern with a new regex, action, and
tests.
@NoahStarkenburg

Copy link
Copy Markdown
Author

Thanks, this is really helpful. I hadn't come across ajv-formats or the W3C XSD regex before, so those links cleared up a lot.

Since #1497 is about OpenAPI format: duration, I went with the ajv-formats regex. It's the profile JSON Schema and OpenAPI tooling actually expect, and it's widely used, so it felt like the safe proven choice. I dropped it in unchanged and only added the u flag to match the other regexes in regex.ts:

/^P(?!$)((\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?|(\d+W)?)$/u

So it accepts the week form (P2W) and PnYnMnDTnHnMnS, but not negatives or fractional values. I moved the fractional cases into the invalid tests, noted the source in regex.ts, and added a hint about the limitation on the action.

On the name, I kept isoDuration to match the existing iso* actions. valibot already handles this kind of split with email and rfcEmail, a practical subset plus a broader variant, so if the XSD profile (negatives and fractional seconds, no weeks) is ever wanted it could go in as a separate action the same way. Happy to rename to rfc3339Duration if you'd rather make the profile explicit. Let me know what you prefer.

NoahStarkenburg and others added 2 commits June 29, 2026 22:05
The regex matches the RFC 3339 Appendix A duration profile, so name the
action accordingly. This leaves room for other duration profiles later.
@NoahStarkenburg NoahStarkenburg changed the title Add isoDuration validation action Add rfc3339Duration validation action Jun 30, 2026
@fabian-hiller

Copy link
Copy Markdown
Member

Thank you! I will try to take a closer look before v1.5.

@yslpn

yslpn commented Jul 18, 2026

Copy link
Copy Markdown
Member

Hi @NoahStarkenburg — thanks for the work here, and for being responsive to feedback so far. I want to lock in the direction for this PR so we can get it merged into v1.5. A few things needed before we merge:

1. Document why fractional/negative values are rejected

Right now the code doesn't explain why the regex rejects fractional (P0.5Y) and negative (-P1D) durations — that context only exists in the PR comment history. Please add it to the code so it doesn't get lost or "fixed" back later:

  • In the JSDoc for the action, add a short note along these lines:

    /**
     * Creates an RFC 3339 duration validation action.
     *
     * Note: Fractional (e.g. `P0.5Y`) and negative (e.g. `-P1D`) durations are
     * intentionally rejected to match the `ajv-formats` `duration` format,
     * which most JSON Schema / OpenAPI tooling relies on for `format: duration`.
     *
     * ...
     */
  • Keep the existing attribution comment on the regex constant pointing to ajv-formats (MIT) — please don't drop it.

2. Naming: rfc3339DurationrfcDuration

Looking at our existing rfcEmail action (and RFC_EMAIL_REGEX), the convention for RFC-based validators is to drop the specific RFC number from the name — the JSDoc/regex comment carries that detail instead. Could you rename rfc3339DurationrfcDuration (and RFC_3339_DURATION_REGEXRFC_DURATION_REGEX) to match? Same applies to the issue/action type names (Rfc3339DurationIssueRfcDurationIssue, etc.).

3. Test coverage against the ajv-formats reference behavior

Please add a few explicit test cases that document the intentional divergence from "full" ISO 8601 / strict RFC 3339 ABNF, e.g.:

  • P1Y1D (year + day, skipping month) → accepted (ajv-formats doesn't nest components; a stricter ABNF reading would reject this — worth a comment in the test explaining why we follow ajv here)
  • P0.5Y, P0,5Y, -P1Drejected (documents that we intentionally don't extend beyond the OpenAPI-relevant profile)

This makes the design decision self-documenting for future readers/reviewers.

4. Website docs

This PR is missing the usual API reference additions (properties.ts + index.mdx under website/src/routes/api/, menu.md entry, "Related" cross-links).

Once these are in, I think this is ready to merge. Thanks again for sticking with it through the back-and-forth!

@pkg-pr-new

pkg-pr-new Bot commented Jul 26, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/valibot@1520

commit: d56f172

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: RFC 3339 duration Validator

3 participants