agent_patch
runs/2026-02-27__21-30-28__gpt-5-4/flux-pr-4680/flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/agent-logs/agent.patch
23360 bytes
diff --git a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/docs/content/api.mdx b/app/packages/docs/content/api.mdx
index 47e792c..43ad374 100644
--- a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/docs/content/api.mdx
+++ b/app/packages/docs/content/api.mdx
@@ -350,14 +350,16 @@ schema.parse("http://example.com"); // ❌
As you may have noticed, Zod string includes a few date/time related validations. These validations are regular expression based, so they are not as strict as a full date/time library. However, they are very convenient for validating user input.
-The `z.iso.datetime()` method enforces ISO 8601; by default, no timezone offsets are allowed:
+The `z.iso.datetime()` method validates ISO 8601 datetimes. By default it accepts a trailing `Z`, allows minute-only values like `HH:MMZ`, and makes seconds and fractional seconds optional:
```ts
const datetime = z.iso.datetime();
+datetime.parse("2020-01-01T00:00Z"); // ✅
datetime.parse("2020-01-01T00:00:00Z"); // ✅
datetime.parse("2020-01-01T00:00:00.123Z"); // ✅
datetime.parse("2020-01-01T00:00:00.123456Z"); // ✅ (arbitrary precision)
+datetime.parse("2020-01-01T00:00"); // ❌ (local time disabled)
datetime.parse("2020-01-01T00:00:00+02:00"); // ❌ (no offsets allowed)
```
@@ -366,30 +368,38 @@ To allow timezone offsets:
```ts
const datetime = z.iso.datetime({ offset: true });
-// result is normalized to RFC 3339 format
-datetime.parse("2020-01-01T00:00:00+02"); // ✅ "2020-01-01T00:00:00+02:00"
-datetime.parse("2020-01-01T00:00:00+0200"); // ✅ "2020-01-01T00:00:00+02:00"
-datetime.parse("2020-01-01T00:00:00+02:00"); // ✅ "2020-01-01T00:00:00+02:00"
+datetime.parse("2020-01-01T00:00+02:00"); // ✅
+datetime.parse("2020-01-01T00:00:00+02"); // ✅
+datetime.parse("2020-01-01T00:00:00+0200"); // ✅
+datetime.parse("2020-01-01T00:00:00+02:00"); // ✅
// Z is still supported
-datetime.parse("2020-01-01T00:00:00Z"); // ✅
+datetime.parse("2020-01-01T00:00:00Z"); // ✅
```
To allow unqualified (timezone-less) datetimes:
```ts
const schema = z.iso.datetime({ local: true });
+schema.parse("2020-01-01T00:00"); // ✅
schema.parse("2020-01-01T00:00:00"); // ✅
```
-To constrain the allowable `precision` (by default, arbitrary sub-second precision is supported).
+To constrain `precision`, use one of the named levels from `z.TimePrecision` or pass the number directly:
```ts
-const datetime = z.iso.datetime({ precision: 3 });
+const a = z.iso.datetime({ precision: z.TimePrecision.Minute });
+const b = z.iso.datetime({ precision: -1 });
+const c = z.iso.datetime({ precision: z.TimePrecision.Second });
+const d = z.iso.datetime({ precision: z.TimePrecision.Millisecond });
-datetime.parse("2020-01-01T00:00:00.123Z"); // ✅
-datetime.parse("2020-01-01T00:00:00Z"); // ❌
-datetime.parse("2020-01-01T00:00:00.123456Z"); // ❌
+a.parse("2020-01-01T00:00Z"); // ✅
+b.parse("2020-01-01T00:00Z"); // ✅
+c.parse("2020-01-01T00:00:00Z"); // ✅
+d.parse("2020-01-01T00:00:00.123Z"); // ✅
+
+d.parse("2020-01-01T00:00:00Z"); // ❌
+d.parse("2020-01-01T00:00:00.123456Z"); // ❌
```
### ISO dates
@@ -408,11 +418,12 @@ date.parse("2020-01-32"); // ❌
> Added in Zod 3.23
-The `z.iso.time()` method validates strings in the format `HH:MM:SS[.s+]`. The second can include arbitrary decimal precision. It does not allow timezone offsets of any kind.
+The `z.iso.time()` method validates strings in the format `HH:MM` or `HH:MM:SS[.s+]`. It does not allow timezone offsets of any kind.
```ts
const time = z.iso.time();
+time.parse("00:00"); // ✅
time.parse("00:00:00"); // ✅
time.parse("09:52:31"); // ✅
time.parse("23:59:59.9999999"); // ✅ (arbitrary precision)
@@ -421,14 +432,18 @@ time.parse("00:00:00.123Z"); // ❌ (no `Z` allowed)
time.parse("00:00:00.123+02:00"); // ❌ (no offsets allowed)
```
-You can set the `precision` option to constrain the allowable decimal precision.
+You can set `precision` to require a specific level.
```ts
-const time = z.iso.time({ precision: 3 });
+z.iso.time({ precision: -1 }); // HH:MM (minute precision)
+z.iso.time({ precision: 0 }); // HH:MM:SS (second precision)
+z.iso.time({ precision: 3 }); // HH:MM:SS.sss (millisecond precision)
-time.parse("00:00:00.123"); // ✅
-time.parse("00:00:00.123456"); // ❌
+const time = z.iso.time({ precision: z.TimePrecision.Microsecond });
+
+time.parse("00:00:00.123456"); // ✅
time.parse("00:00:00"); // ❌
+time.parse("00:00:00.123"); // ❌
```
### IP addresses
@@ -2542,4 +2557,3 @@ const MyFunction = z.function({
const computeTrimmedLength = MyFunction.implement((input) => input.trim.length);
```
-
diff --git a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/classic/external.ts b/app/packages/zod/src/v4/classic/external.ts
index eccc470..9a0f47e 100644
--- a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/classic/external.ts
+++ b/app/packages/zod/src/v4/classic/external.ts
@@ -22,6 +22,7 @@ export {
$brand,
clone,
regexes,
+ TimePrecision,
treeifyError,
prettifyError,
formatError,
diff --git a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/classic/tests/index.test.ts b/app/packages/zod/src/v4/classic/tests/index.test.ts
index 2f24eb6..a5b502e 100644
--- a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/classic/tests/index.test.ts
+++ b/app/packages/zod/src/v4/classic/tests/index.test.ts
@@ -82,10 +82,10 @@ test("z.coerce.date", () => {
});
test("z.iso.datetime", () => {
- const d1 = "2021-01-01T00:00:00Z";
+ const d1 = "2021-01-01T00:00Z";
const d2 = "2021-01-01T00:00:00.123Z";
const d3 = "2021-01-01T00:00:00";
- const d4 = "2021-01-01T00:00:00+07:00";
+ const d4 = "2021-01-01T00:00+07:00";
const d5 = "bad data";
// local: false, offset: false, precision: null
@@ -116,6 +116,13 @@ test("z.iso.datetime", () => {
expect(z.safeParse(d, d3).success).toEqual(false);
expect(z.safeParse(d, d4).success).toEqual(false);
expect(z.safeParse(d, d5).success).toEqual(false);
+
+ const e = z.iso.datetime({ precision: z.TimePrecision.Minute });
+ expect(z.safeParse(e, d1).success).toEqual(true);
+ expect(z.safeParse(e, d2).success).toEqual(false);
+ expect(z.safeParse(e, d3).success).toEqual(false);
+ expect(z.safeParse(e, d4).success).toEqual(false);
+ expect(z.safeParse(e, d5).success).toEqual(false);
});
test("z.iso.date", () => {
@@ -132,7 +139,7 @@ test("z.iso.date", () => {
});
test("z.iso.time", () => {
- const d1 = "00:00:00";
+ const d1 = "00:00";
const d2 = "00:00:00.123";
const d3 = "bad data";
@@ -150,6 +157,11 @@ test("z.iso.time", () => {
expect(z.safeParse(c, d1).success).toEqual(true);
expect(z.safeParse(c, d2).success).toEqual(true);
expect(z.safeParse(c, d3).success).toEqual(false);
+
+ const d = z.iso.time({ precision: z.TimePrecision.Minute });
+ expect(z.safeParse(d, d1).success).toEqual(true);
+ expect(z.safeParse(d, d2).success).toEqual(false);
+ expect(z.safeParse(d, d3).success).toEqual(false);
});
test("z.iso.duration", () => {
diff --git a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/classic/tests/string.test.ts b/app/packages/zod/src/v4/classic/tests/string.test.ts
index 0de8bee..9fe275d 100644
--- a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/classic/tests/string.test.ts
+++ b/app/packages/zod/src/v4/classic/tests/string.test.ts
@@ -755,6 +755,7 @@ test("lowerCase", () => {
test("datetime parsing", () => {
const datetime = z.string().datetime();
+ datetime.parse("1970-01-01T00:00Z");
datetime.parse("1970-01-01T00:00:00.000Z");
datetime.parse("2022-10-13T09:52:31.816Z");
datetime.parse("2022-10-13T09:52:31.8162314Z");
@@ -764,12 +765,21 @@ test("datetime parsing", () => {
expect(() => datetime.parse("foo")).toThrow();
expect(() => datetime.parse("2020-10-14")).toThrow();
expect(() => datetime.parse("T18:45:12.123")).toThrow();
+ expect(() => datetime.parse("2020-10-14T17:42")).toThrow();
expect(() => datetime.parse("2020-10-14T17:42:29+00:00")).toThrow();
+ const datetimeMinute = z.string().datetime({ precision: -1 });
+ datetimeMinute.parse("1970-01-01T00:00Z");
+ datetimeMinute.parse("2022-10-13T09:52Z");
+ expect(() => datetimeMinute.parse("tuna")).toThrow();
+ expect(() => datetimeMinute.parse("1970-01-01T00:00:00Z")).toThrow();
+ expect(() => datetimeMinute.parse("2022-10-13T09:52:31.816Z")).toThrow();
+
const datetimeNoMs = z.string().datetime({ precision: 0 });
datetimeNoMs.parse("1970-01-01T00:00:00Z");
datetimeNoMs.parse("2022-10-13T09:52:31Z");
expect(() => datetimeNoMs.parse("tuna")).toThrow();
+ expect(() => datetimeNoMs.parse("1970-01-01T00:00Z")).toThrow();
expect(() => datetimeNoMs.parse("1970-01-01T00:00:00.000Z")).toThrow();
expect(() => datetimeNoMs.parse("1970-01-01T00:00:00.Z")).toThrow();
expect(() => datetimeNoMs.parse("2022-10-13T09:52:31.816Z")).toThrow();
@@ -783,10 +793,12 @@ test("datetime parsing", () => {
expect(() => datetime3Ms.parse("2022-10-13T09:52:31Z")).toThrow();
const datetimeOffset = z.string().datetime({ offset: true });
+ datetimeOffset.parse("2020-10-14T17:42Z");
datetimeOffset.parse("1970-01-01T00:00:00.000Z");
datetimeOffset.parse("2022-10-13T09:52:31.816234134Z");
datetimeOffset.parse("1970-01-01T00:00:00Z");
datetimeOffset.parse("2022-10-13T09:52:31.4Z");
+ datetimeOffset.parse("2020-10-14T17:42+00:00");
datetimeOffset.parse("2020-10-14T17:42:29+00:00");
datetimeOffset.parse("2020-10-14T17:42:29+03:15");
datetimeOffset.parse("2020-10-14T17:42:29+0315");
@@ -801,11 +813,21 @@ test("datetime parsing", () => {
datetimeOffsetNoMs.parse("2020-10-14T17:42:29+0000");
datetimeOffsetNoMs.parse("2020-10-14T17:42:29+00");
expect(() => datetimeOffsetNoMs.parse("tuna")).toThrow();
+ expect(() => datetimeOffsetNoMs.parse("1970-01-01T00:00Z")).toThrow();
expect(() => datetimeOffsetNoMs.parse("1970-01-01T00:00:00.000Z")).toThrow();
expect(() => datetimeOffsetNoMs.parse("1970-01-01T00:00:00.Z")).toThrow();
expect(() => datetimeOffsetNoMs.parse("2022-10-13T09:52:31.816Z")).toThrow();
expect(() => datetimeOffsetNoMs.parse("2020-10-14T17:42:29.124+00:00")).toThrow();
+ const datetimeOffsetMinute = z.string().datetime({ offset: true, precision: -1 });
+ datetimeOffsetMinute.parse("1970-01-01T00:00Z");
+ datetimeOffsetMinute.parse("2020-10-14T17:42+00:00");
+ datetimeOffsetMinute.parse("2020-10-14T17:42+0000");
+ datetimeOffsetMinute.parse("2020-10-14T17:42+00");
+ expect(() => datetimeOffsetMinute.parse("tuna")).toThrow();
+ expect(() => datetimeOffsetMinute.parse("1970-01-01T00:00:00Z")).toThrow();
+ expect(() => datetimeOffsetMinute.parse("2020-10-14T17:42:29+00:00")).toThrow();
+
const datetimeOffset4Ms = z.string().datetime({ offset: true, precision: 4 });
datetimeOffset4Ms.parse("1970-01-01T00:00:00.1234Z");
datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+00:00");
@@ -816,7 +838,7 @@ test("datetime parsing", () => {
expect(() => datetimeOffset4Ms.parse("2020-10-14T17:42:29.124+00:00")).toThrow();
});
-test("datetime offset normalization", () => {
+test("datetime offset preservation", () => {
const a = z.iso.datetime({ offset: true });
expect({
a: a.parse("2020-10-14T17:42:29+02"),
@@ -824,8 +846,8 @@ test("datetime offset normalization", () => {
c: a.parse("2020-10-14T17:42:29+02:00"),
}).toMatchInlineSnapshot(`
{
- "a": "2020-10-14T17:42:29+02:00",
- "b": "2020-10-14T17:42:29+02:00",
+ "a": "2020-10-14T17:42:29+02",
+ "b": "2020-10-14T17:42:29+0200",
"c": "2020-10-14T17:42:29+02:00",
}
`);
@@ -887,6 +909,7 @@ test("date parsing", () => {
test("time parsing", () => {
const time = z.string().time();
+ time.parse("00:00");
time.parse("00:00:00");
time.parse("23:00:00");
time.parse("00:59:00");
@@ -898,6 +921,7 @@ test("time parsing", () => {
expect(() => time.parse("foo")).toThrow();
expect(() => time.parse("00:00:00Z")).toThrow();
expect(() => time.parse("0:00:00")).toThrow();
+ expect(() => time.parse("0:00")).toThrow();
expect(() => time.parse("00:0:00")).toThrow();
expect(() => time.parse("00:00:0")).toThrow();
expect(() => time.parse("00:00:00.000+00:00")).toThrow();
@@ -907,6 +931,15 @@ test("time parsing", () => {
expect(() => time.parse("00:00:60")).toThrow();
expect(() => time.parse("24:60:60")).toThrow();
+ const timeMinute = z.string().time({ precision: -1 });
+ timeMinute.parse("00:00");
+ timeMinute.parse("09:52");
+ timeMinute.parse("23:59");
+ expect(() => timeMinute.parse("")).toThrow();
+ expect(() => timeMinute.parse("foo")).toThrow();
+ expect(() => timeMinute.parse("00:00:00")).toThrow();
+ expect(() => timeMinute.parse("00:00Z")).toThrow();
+
const time2 = z.string().time({ precision: 2 });
time2.parse("00:00:00.00");
time2.parse("09:52:31.12");
diff --git a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/classic/tests/template-literal.test.ts b/app/packages/zod/src/v4/classic/tests/template-literal.test.ts
index e46dccd..be61ecd 100644
--- a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/classic/tests/template-literal.test.ts
+++ b/app/packages/zod/src/v4/classic/tests/template-literal.test.ts
@@ -551,7 +551,7 @@ test("regexes", () => {
expect(cuidZZZ._zod.pattern.source).toMatchInlineSnapshot(`"^[cC][^\\s-]{8,}ZZZ$"`);
expect(cuid2._zod.pattern.source).toMatchInlineSnapshot(`"^[0-9a-z]+$"`);
expect(datetime._zod.pattern.source).toMatchInlineSnapshot(
- `"^((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))T([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(\\.\\d+)?(Z)$"`
+ `"^((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))T([01]\\d|2[0-3]):[0-5]\\d(:[0-5]\\d(\\.\\d+)?)?(Z)$"`
);
expect(email._zod.pattern.source).toMatchInlineSnapshot(
`"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"`
diff --git a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/classic/tests/to-json-schema.test.ts b/app/packages/zod/src/v4/classic/tests/to-json-schema.test.ts
index 7c32b03..9ab1bd4 100644
--- a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/classic/tests/to-json-schema.test.ts
+++ b/app/packages/zod/src/v4/classic/tests/to-json-schema.test.ts
@@ -62,7 +62,7 @@ describe("toJSONSchema", () => {
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "date-time",
- "pattern": "^((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))T([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(\\.\\d+)?(Z)$",
+ "pattern": "^((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))T([01]\\d|2[0-3]):[0-5]\\d(:[0-5]\\d(\\.\\d+)?)?(Z)$",
"type": "string",
}
`);
@@ -78,7 +78,7 @@ describe("toJSONSchema", () => {
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "time",
- "pattern": "^([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(\\.\\d+)?$",
+ "pattern": "^([01]\\d|2[0-3]):[0-5]\\d(:[0-5]\\d(\\.\\d+)?)?$",
"type": "string",
}
`);
@@ -277,7 +277,7 @@ describe("toJSONSchema", () => {
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "date-time",
- "pattern": "^((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))T([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(\\.\\d+)?(Z)$",
+ "pattern": "^((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))T([01]\\d|2[0-3]):[0-5]\\d(:[0-5]\\d(\\.\\d+)?)?(Z)$",
"type": "string",
}
`);
@@ -294,7 +294,7 @@ describe("toJSONSchema", () => {
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "time",
- "pattern": "^([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(\\.\\d+)?$",
+ "pattern": "^([01]\\d|2[0-3]):[0-5]\\d(:[0-5]\\d(\\.\\d+)?)?$",
"type": "string",
}
`);
diff --git a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/core/api.ts b/app/packages/zod/src/v4/core/api.ts
index ab6efb7..03f9841 100644
--- a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/core/api.ts
+++ b/app/packages/zod/src/v4/core/api.ts
@@ -451,6 +451,14 @@ export function _jwt<T extends schemas.$ZodJWT>(
}
// ISODateTime
+export const TimePrecision = {
+ Any: null,
+ Minute: -1,
+ Second: 0,
+ Millisecond: 3,
+ Microsecond: 6,
+} as const;
+
export type $ZodISODateTimeParams = StringFormatParams<schemas.$ZodISODateTime, "pattern">;
export type $ZodCheckISODateTimeParams = CheckStringFormatParams<schemas.$ZodISODateTime, "pattern">;
export function _isoDateTime<T extends schemas.$ZodISODateTime>(
diff --git a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/core/regexes.ts b/app/packages/zod/src/v4/core/regexes.ts
index 9cc1d48..54543b0 100644
--- a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/core/regexes.ts
+++ b/app/packages/zod/src/v4/core/regexes.ts
@@ -81,15 +81,18 @@ const dateSource = `((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][04
export const date: RegExp = /*@__PURE__*/ new RegExp(`^${dateSource}$`);
function timeSource(args: { precision?: number | null }) {
- // let regex = `\\d{2}:\\d{2}:\\d{2}`;
- let regex = `([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d`;
+ const prefix = `([01]\\d|2[0-3]):[0-5]\\d`;
+ if (args.precision === -1) return prefix;
- if (args.precision) {
- regex = `${regex}\\.\\d{${args.precision}}`;
+ let seconds = `[0-5]\\d`;
+ if (args.precision && args.precision > 0) {
+ seconds = `${seconds}\\.\\d{${args.precision}}`;
} else if (args.precision == null) {
- regex = `${regex}(\\.\\d+)?`;
+ seconds = `${seconds}(\\.\\d+)?`;
}
- return regex;
+
+ const secondsQuantifier = args.precision == null ? "?" : "";
+ return `${prefix}(:${seconds})${secondsQuantifier}`;
}
export function time(args: {
precision?: number | null;
@@ -106,9 +109,8 @@ export function datetime(args: {
let regex = `${dateSource}T${timeSource(args)}`;
const opts: string[] = [];
- opts.push(args.local ? `Z?` : `Z`);
- // if (args.offset) opts.push(`([+-]\\d{2}:?\\d{2})`);
- // minutes, colon optional
+ if (args.local) opts.push(`Z?`);
+ else opts.push(`Z`);
if (args.offset) opts.push(`([+-]\\d{2}(?::?\\d{2})?)`);
regex = `${regex}(${opts.join("|")})`;
return new RegExp(`^${regex}$`);
diff --git a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/core/schemas.ts b/app/packages/zod/src/v4/core/schemas.ts
index e2ace79..0ee0032 100644
--- a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/core/schemas.ts
+++ b/app/packages/zod/src/v4/core/schemas.ts
@@ -604,20 +604,6 @@ export const $ZodISODateTime: core.$constructor<$ZodISODateTime> = /*@__PURE__*/
(inst, def): void => {
def.pattern ??= regexes.datetime(def);
$ZodStringFormat.init(inst, def);
-
- const _super = inst._zod.check;
- inst._zod.check = (payload) => {
- _super(payload);
-
- // normalize timezone offset
- // add colon & minutes if missing
- // if no offset, return early
- const curr = payload.value;
- if (/[+-]\d\d$/.test(curr)) payload.value = curr + ":00";
- else if (/[+-]\d\d\d\d$/.test(curr)) {
- payload.value = curr.slice(0, -2) + ":" + curr.slice(-2);
- }
- };
}
);
diff --git a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/mini/external.ts b/app/packages/zod/src/v4/mini/external.ts
index 4b23cf4..8bd1760 100644
--- a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/mini/external.ts
+++ b/app/packages/zod/src/v4/mini/external.ts
@@ -14,6 +14,7 @@ export {
function,
clone,
regexes,
+ TimePrecision,
treeifyError,
prettifyError,
formatError,
diff --git a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/mini/tests/index.test.ts b/app/packages/zod/src/v4/mini/tests/index.test.ts
index 7563865..77c08cc 100644
--- a/tmp/agent-patch-flux-pr-4680.1-of-1.2026-02-27__21-30-28__gpt-5-4/app/packages/zod/src/v4/mini/tests/index.test.ts
+++ b/app/packages/zod/src/v4/mini/tests/index.test.ts
@@ -82,10 +82,10 @@ test("z.coerce.date", () => {
});
test("z.iso.datetime", () => {
- const d1 = "2021-01-01T00:00:00Z";
+ const d1 = "2021-01-01T00:00Z";
const d2 = "2021-01-01T00:00:00.123Z";
const d3 = "2021-01-01T00:00:00";
- const d4 = "2021-01-01T00:00:00+07:00";
+ const d4 = "2021-01-01T00:00+07:00";
const d5 = "bad data";
// local: false, offset: false, precision: null
@@ -116,6 +116,13 @@ test("z.iso.datetime", () => {
expect(z.safeParse(d, d3).success).toEqual(false);
expect(z.safeParse(d, d4).success).toEqual(false);
expect(z.safeParse(d, d5).success).toEqual(false);
+
+ const e = z.iso.datetime({ precision: z.TimePrecision.Minute });
+ expect(z.safeParse(e, d1).success).toEqual(true);
+ expect(z.safeParse(e, d2).success).toEqual(false);
+ expect(z.safeParse(e, d3).success).toEqual(false);
+ expect(z.safeParse(e, d4).success).toEqual(false);
+ expect(z.safeParse(e, d5).success).toEqual(false);
});
test("z.iso.date", () => {
@@ -132,7 +139,7 @@ test("z.iso.date", () => {
});
test("z.iso.time", () => {
- const d1 = "00:00:00";
+ const d1 = "00:00";
const d2 = "00:00:00.123";
const d3 = "bad data";
@@ -150,6 +157,11 @@ test("z.iso.time", () => {
expect(z.safeParse(c, d1).success).toEqual(true);
expect(z.safeParse(c, d2).success).toEqual(true);
expect(z.safeParse(c, d3).success).toEqual(false);
+
+ const d = z.iso.time({ precision: z.TimePrecision.Minute });
+ expect(z.safeParse(d, d1).success).toEqual(true);
+ expect(z.safeParse(d, d2).success).toEqual(false);
+ expect(z.safeParse(d, d3).success).toEqual(false);
});
test("z.iso.duration", () => {