Skip to content

Action isoTimestamp allows space in UTC offset#1195

Merged
fabian-hiller merged 4 commits into
open-circle:mainfrom
frenzzy:iso-timestamp-offset-space
Mar 13, 2026
Merged

Action isoTimestamp allows space in UTC offset#1195
fabian-hiller merged 4 commits into
open-circle:mainfrom
frenzzy:iso-timestamp-offset-space

Conversation

@frenzzy
Copy link
Copy Markdown
Contributor

@frenzzy frenzzy commented May 15, 2025

Fixes:

Changes:

  • Updated ISO 8601 timestamp regex to allow a single space before timezone offset (e.g., +00:00).

Reason:

  • PostgreSQL’s timestamptz default output includes a space before the timezone offset:
    SELECT NOW()::timestamptz;
    -- → '2025-05-13 14:15:41.123904 +00:00'
  • This aligns with the SQL standard output format, even though it’s not strictly ISO 8601 compliant.
  • The change improves compatibility with real PostgreSQL data and prevents false validation errors.

Summary by CodeRabbit

  • Bug Fixes

    • ISO timestamp parsing now accepts an optional space before timezone offsets (e.g., "2020-12-31T23:59:59 +02:00"), reducing false validation failures.
  • Tests

    • Added tests covering UTC offset formats with space separators, including edge cases with multiple spaces, for typed and untyped data.
  • Documentation

    • Updated docs, JSDoc, and changelog to note allowance of a space before UTC offsets.

Copilot AI review requested due to automatic review settings May 15, 2025 09:30
@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
valibot ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 28, 2025 8:22pm

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR updates the ISO 8601 timestamp regex to accept a single space before the timezone offset for better compatibility with PostgreSQL’s timestamptz output.

  • Allow an optional space before the UTC offset in ISO_TIMESTAMP_REGEX.
  • Add tests covering timestamps with a space before the offset.

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
library/src/regex.ts Updated ISO_TIMESTAMP_REGEX to accept a leading space before +/- offset.
library/src/actions/isoTimestamp/isoTimestamp.test.ts Added a test suite for timestamps with a space before the UTC offset.

@fabian-hiller
Copy link
Copy Markdown
Member

Thank you for creating this PR! Are you aware of any drawbacks? I'm a bit concerned because it doesn't seem to be ISO 8601 compliant.

@fabian-hiller fabian-hiller self-assigned this May 16, 2025
@fabian-hiller fabian-hiller added the enhancement New feature or request label May 16, 2025
@frenzzy
Copy link
Copy Markdown
Contributor Author

frenzzy commented May 18, 2025

Thank you for asking! I agree this tweak isn’t strictly ISO 8601, but I don’t anticipate any serious drawbacks, mainly because it matches common real-world usage. For example, PostgreSQL’s timestamptz has for years output a space before the offset by default (following the SQL standard formatting). This means strings like "2025-05-13 14:15:41.123904 +00:00" are already out there – even modern JavaScript engines handle that format just fine (Date.parse() / new Date() don’t error on it in my testing).

Also, our current ISO_TIMESTAMP_REGEX isn’t a fully strict ISO 8601 validator anyway – we’ve already allowed a space instead of "T" between date and time, and the timezone offset was designed to be optional. In that light, permitting a leading space before the +/- offset is consistent with our pragmatic approach. It lets developers validate timestamps exactly as PostgreSQL emits them without having to pre-process the string or write a custom regex.

In short, this change improves compatibility with real data (avoiding false validation errors on perfectly valid SQL-standard timestamps) while staying aligned with the library’s goal of being practical and developer-friendly.

@fabian-hiller
Copy link
Copy Markdown
Member

Sorry for my late reply! I agree but I want to look at other schemas library like Zod first to see how they handle it. For now, I would plan to release this as a "fix" with Valibot v1.2.

@fabian-hiller fabian-hiller added this to the v1.2 milestone May 29, 2025
@frenzzy frenzzy force-pushed the iso-timestamp-offset-space branch from 4abb005 to c45337d Compare June 30, 2025 09:13
@dosubot dosubot Bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Jun 30, 2025
@frenzzy frenzzy force-pushed the iso-timestamp-offset-space branch from c45337d to 0e149f2 Compare July 28, 2025 20:19
@frenzzy frenzzy force-pushed the iso-timestamp-offset-space branch from 0e149f2 to 3fae6fa Compare November 25, 2025 15:17
@vercel
Copy link
Copy Markdown

vercel Bot commented Nov 25, 2025

@frenzzy is attempting to deploy a commit to the Valibot Team on Vercel.

A member of the Team first needs to authorize it.

@fabian-hiller fabian-hiller modified the milestones: v1.4, v1.3 Jan 3, 2026
@fabian-hiller
Copy link
Copy Markdown
Member

Sorry for the delay! I will try to look into it when working on Valibot v1.3.

@fabian-hiller
Copy link
Copy Markdown
Member

@coderabbitai please review this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 7, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the ISO timestamp action/regex file
fd -t f "isoTimestamp" --exec echo {}
rg -n "ISO_TIMESTAMP_REGEX\|isoTimestamp" --type ts -l

Length of output: 164


🌐 Web query:

Zod isoDatetime PostgreSQL timestamptz space offset validation

💡 Result:

PostgreSQL timestamptz commonly outputs timestamps like 2026-03-07 12:34:56+00 (note the space instead of T, and often an offset like +00), even though it will accept ISO-8601 input with T. [1] That output often fails Zod’s ISO datetime validators, which expect ISO-8601/RFC3339-style strings (notably T, and if you allow offsets, typically Z or ±HH:MM). [2][3]

Zod: allow offsets (but still expects T)

const S = z.string().datetime({ offset: true }); // or: z.iso.datetime({ offset: true })

Zod’s datetime() enforces ISO-8601; timezone offsets are only allowed when offset: true. [2][3]

Fix options

A) Return ISO strings from Postgres (recommended)

  • You generally can’t make Postgres globally emit “proper ISO with T” beyond limited datestyle; use explicit formatting instead. [4]
  • Format in SQL, e.g. with to_char(...) to include T and a colonized offset:
select to_char(my_timestamptz, 'YYYY-MM-DD"T"HH24:MI:SSOF') as iso;

Using to_json(...) / row_to_json(...) is also commonly used to get ISO-8601-style timestamp strings in JSON output. [5]

B) Normalize the Postgres output before Zod validation

  • Replace the separator and normalize the offset:
    • " ""T"
    • +00+00:00 (and similarly -07-07:00)
      Then validate with datetime({ offset: true }). (This is needed because Postgres’ default text output uses a space rather than T.) [1][2]

Sources: PostgreSQL datetime output behavior [1]; Zod datetime() and offset option [2][3]; Postgres configurability limits [4]; to_json / JSON ISO formatting notes [5].


🏁 Script executed:

#!/bin/bash
echo "=== isoTimestamp.ts ===" 
cat library/src/actions/isoTimestamp/isoTimestamp.ts

echo ""
echo "=== isoTimestamp.test.ts ===" 
cat library/src/actions/isoTimestamp/isoTimestamp.test.ts

# Also check if there's a regex.ts or similar shared file
echo ""
echo "=== Searching for ISO_TIMESTAMP_REGEX definition ==="
rg -n "ISO_TIMESTAMP_REGEX" --type ts -A 3 -B 1

# Check if there's a changelog or related PR files
echo ""
echo "=== Git diff of PR ==="
git diff main...HEAD -- library/src/actions/isoTimestamp/

Length of output: 18202


@fabian-hiller Here is a full review of this PR.


Review of isoTimestamp — Space Before UTC Offset

Summary of Changes

  1. library/src/regex.ts (line 110) — The timezone part (?:Z|[+-]...) was changed to (?:Z| ?[+-]...), adding an optional space ( ?) before +/- in the offset.
  2. library/src/actions/isoTimestamp/isoTimestamp.test.ts — Fixed two double-// comment typos and added a new test block for the space-before-offset behavior.

Issues Found

1. 🟡 JSDoc is not updated to document the new behavior

isoTimestamp.ts has a dedicated "Hint" for the existing space-as-separator behavior but nothing for the new space-before-offset. This leaves users unaware of the extended format support.

Suggestion — add a new hint to the JSDoc:

  * Hint: The regex also allows a space as a separator between the date and time
  * parts instead of the "T" character.
+ *
+ * Hint: The regex also allows a single space before the timezone offset (e.g.,
+ * "2023-07-11 17:26:27.243 +00:00"), which is consistent with PostgreSQL's
+ * default `timestamptz` output format.

2. 🟡 Missing test coverage for T + space-before-offset combination

The new test block only covers timestamps where both the date/time separator is a space and there's a space before the offset (the PostgreSQL case). However, the regex equally permits a T separator combined with a space before the offset (e.g. 2023-07-11T17:26:27.243 +12:34). This combination is untested.

Either add tests to confirm this is intentionally supported:

test('for T separator with space before UTC offset', () => {
  expectNoActionIssue(action, [
    '2023-07-11T17:26:27.243 +12:34',
    '2023-07-11T17:26:27.243 +1234',
    '2023-07-11T17:26:27.243 +12',
    '2023-07-11T17:26:27.243 -12:34',
  ]);
});

Or, if it's intentionally disallowed (since the main motivation is PostgreSQL which always uses a space separator), constrain the regex so a space before the offset is only allowed when the date/time separator is also a space:

- /^\d{4}-(?:0[1-9]|1[0-2])-(?:[12]\d|0[1-9]|3[01])[T ](?:0\d|1\d|2[0-3])(?::[0-5]\d){2}(?:\.\d{1,9})?(?:Z| ?[+-](?:0\d|1\d|2[0-3])(?::?[0-5]\d)?)$/u
+ /^\d{4}-(?:0[1-9]|1[0-2])-(?:[12]\d|0[1-9]|3[01])(?:T(?:0\d|1\d|2[0-3])(?::[0-5]\d){2}(?:\.\d{1,9})?(?:Z|[+-](?:0\d|1\d|2[0-3])(?::?[0-5]\d)?)|\ (?:0\d|1\d|2[0-3])(?::[0-5]\d){2}(?:\.\d{1,9})?(?:Z| ?[+-](?:0\d|1\d|2[0-3])(?::?[0-5]\d)?))$/u

The simpler path is probably to just add tests documenting the T + space-before-offset as valid, unless there's a reason to disallow it.


3. ✅ Nitpick: Double-// comment fix is correct

The fix from // // '+hh'// '+hh' (and same for -hh) is a clean, valid correction.


Comparison with Zod

Zod's datetime() enforces ISO 8601 strictly and does not allow a space before the offset — its recommendation is to preprocess PostgreSQL output (e.g., replace " ""T") before validation, rather than widening the regex.

Valibot's approach here is pragmatic and consistent with the already-existing allowance of a space as the date/time separator. The change is small and the impact is well-contained.


Overall

The core change is sound. The two main action items before merging are:

  1. Add the new JSDoc hint in isoTimestamp.ts.
  2. Clarify (with tests or a tighter regex) whether T + space-before-offset is intentionally supported.
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 7, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 64953cd4-6b3a-4d4c-b258-0feaa7047f0f

📥 Commits

Reviewing files that changed from the base of the PR and between 428666d and cbfeab8.

📒 Files selected for processing (4)
  • library/src/actions/isoTimestamp/isoTimestamp.test.ts
  • library/src/actions/isoTimestamp/isoTimestamp.ts
  • library/src/regex.ts
  • website/src/routes/api/(actions)/isoTimestamp/index.mdx
🚧 Files skipped from review as they are similar to previous changes (3)
  • library/src/actions/isoTimestamp/isoTimestamp.test.ts
  • library/src/regex.ts
  • website/src/routes/api/(actions)/isoTimestamp/index.mdx

📝 Walkthrough

Walkthrough

ISO timestamp validation was updated to accept an optional space before the timezone offset and to allow a space as the date/time separator; tests were added for space-separated UTC offsets and some test comments were realigned.

Changes

Cohort / File(s) Summary
Regex Pattern Enhancement
library/src/regex.ts
Adjusted ISO_TIMESTAMP_REGEX to accept a space as the date/time separator and an optional space before the timezone offset (supports Z, ±hh:mm, and ±hhmm variants with optional space).
Test Coverage Addition
library/src/actions/isoTimestamp/isoTimestamp.test.ts
Added tests for timestamps that include a space before the UTC offset (both untyped and typed datasets) and added edge cases with two spaces before offsets; minor comment formatting fixes.
Docs & Changelog
library/src/actions/isoTimestamp/isoTimestamp.ts, website/src/routes/api/(actions)/isoTimestamp/index.mdx, library/CHANGELOG.md
Updated JSDoc and website docs to document acceptance of a space before UTC offsets; added changelog entry describing the validation change.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 I nudged the pattern, soft and spry,
Let a tiny space slip quietly by,
Offsets now hug with room to spare,
Timestamps grin beneath moonlit air,
Hop — the tests all cheer and sigh! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: allowing a space before the UTC offset in ISO timestamp validation for PostgreSQL compatibility.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
library/src/regex.ts (1)

106-110: Consider updating JSDoc to document the PostgreSQL-style space allowance.

The regex now accepts an optional space before the timezone offset (e.g., 2023-07-11 17:26:27.243 +00:00), which deviates from strict ISO 8601. Since the library already takes a pragmatic approach (allowing space instead of T), documenting this extension helps users understand the accepted formats.

📝 Proposed JSDoc update
 /**
- * [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp regex.
+ * [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp regex.
+ *
+ * Hint: This regex allows a space instead of `T` as the date/time separator
+ * and an optional space before the timezone offset for PostgreSQL compatibility.
  */
 export const ISO_TIMESTAMP_REGEX: RegExp =
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@library/src/regex.ts` around lines 106 - 110, Update the JSDoc for
ISO_TIMESTAMP_REGEX to note the pragmatic extensions: mention that it accepts
either 'T' or a space between date and time and explicitly allows an optional
space before the timezone offset (PostgreSQL-style timestamps like "2023-07-11
17:26:27.243 +00:00"), clarifying that this deviates from strict ISO 8601;
reference the exported constant ISO_TIMESTAMP_REGEX and keep the link to ISO
8601 for context.
library/src/actions/isoTimestamp/isoTimestamp.test.ts (1)

130-139: Add test coverage for T separator with space before offset.

The new tests cover the PostgreSQL-style format (space as both date/time separator and before offset). However, the regex also accepts T as separator with a space before the offset (e.g., 2023-07-11T17:26:27.243 +00:00). Consider adding a test case to confirm this combination is intentionally allowed, or update the regex to disallow it if not intended.

🧪 Proposed test addition
     test('for space as separator in UTC offset', () => {
       expectNoActionIssue(action, [
         '0000-01-01 00:00:00.000 +00:00',
         '0000-01-01 00:00:00.000 -00:00',
         '2023-07-11 17:26:27.243 +0000',
         '2023-07-11 17:26:27.243 -0000',
         '9999-12-31 23:59:59.999 +00',
         '9999-12-31 23:59:59.999 -00',
+        // T separator with space before offset
+        '2023-07-11T17:26:27.243 +12:34',
+        '2023-07-11T17:26:27.243 -12:34',
       ]);
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@library/src/actions/isoTimestamp/isoTimestamp.test.ts` around lines 130 -
139, Add an explicit test in isoTimestamp.test.ts to cover the combination of a
literal "T" date/time separator with a space before the UTC offset (e.g.
'2023-07-11T17:26:27.243 +00:00') so we verify whether that pattern is
intentionally accepted; modify the existing test case using
expectNoActionIssue(action, [...]) to include this string (or add a new test
with the same helper) and if the behavior should be disallowed instead, update
the ISO timestamp validation regex in the isoTimestamp validator to reject "T"
followed by a space before the offset.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@library/src/actions/isoTimestamp/isoTimestamp.test.ts`:
- Around line 130-139: Add an explicit test in isoTimestamp.test.ts to cover the
combination of a literal "T" date/time separator with a space before the UTC
offset (e.g. '2023-07-11T17:26:27.243 +00:00') so we verify whether that pattern
is intentionally accepted; modify the existing test case using
expectNoActionIssue(action, [...]) to include this string (or add a new test
with the same helper) and if the behavior should be disallowed instead, update
the ISO timestamp validation regex in the isoTimestamp validator to reject "T"
followed by a space before the offset.

In `@library/src/regex.ts`:
- Around line 106-110: Update the JSDoc for ISO_TIMESTAMP_REGEX to note the
pragmatic extensions: mention that it accepts either 'T' or a space between date
and time and explicitly allows an optional space before the timezone offset
(PostgreSQL-style timestamps like "2023-07-11 17:26:27.243 +00:00"), clarifying
that this deviates from strict ISO 8601; reference the exported constant
ISO_TIMESTAMP_REGEX and keep the link to ISO 8601 for context.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5f2bfa07-4441-41a4-817b-80851f68d7f7

📥 Commits

Reviewing files that changed from the base of the PR and between 053ae97 and 3fae6fa.

📒 Files selected for processing (2)
  • library/src/actions/isoTimestamp/isoTimestamp.test.ts
  • library/src/regex.ts

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread library/src/regex.ts Outdated
Comment thread library/src/actions/isoTimestamp/isoTimestamp.test.ts
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Mar 8, 2026

Open in StackBlitz

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

commit: 253a7d0

@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:S This PR changes 10-29 lines, ignoring generated files. labels Mar 8, 2026
@fabian-hiller fabian-hiller requested a review from Copilot March 8, 2026 22:43
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread library/src/actions/isoTimestamp/isoTimestamp.ts Outdated
Comment thread website/src/routes/api/(actions)/isoTimestamp/index.mdx Outdated
Comment thread library/src/actions/isoTimestamp/isoTimestamp.ts Outdated
Comment thread library/src/actions/isoTimestamp/isoTimestamp.test.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@fabian-hiller fabian-hiller merged commit 50016c7 into open-circle:main Mar 13, 2026
16 of 17 checks passed
@fabian-hiller
Copy link
Copy Markdown
Member

Valibot v1.3 is available. Feel free to submit and expense of $15 for all your contributions for Valibot v1.3 via Open Collective.

@frenzzy frenzzy deleted the iso-timestamp-offset-space branch March 18, 2026 04:44
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:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants