Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support local time if no offset #2522

Merged
merged 1 commit into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 43 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@
- [Coercion for primitives](#coercion-for-primitives)
- [Literals](#literals)
- [Strings](#strings)
- [ISO datetimes](#iso-datetimes)
- [Datetimes](#datetimes)
- [Dates](#dates)
- [Times](#times)
- [IP addresses](#ip-addresses)
- [Numbers](#numbers)
- [BigInts](#bigints)
Expand Down Expand Up @@ -893,18 +895,13 @@ z.string().time({ message: "Invalid time string!" });
z.string().ip({ message: "Invalid IP address" });
```

### ISO Date, Time & Datetime validation
### Datetimes

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.
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.string().date()` method validates strings in the format `YYYY-MM-DD`.

The `z.string().time()` method validates strings in the format `HH:mm:ss[.SSSSSS][Z|(+|-)hh[:]mm]`
(the time portion of [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)). It defaults
to `HH:mm:ss[.SSSSSS]` validation: no timezone offsets or `Z`, with arbitrary sub-second
decimal.
The `z.string().time()` method validates strings in the format `HH:mm:ss[.SSSSSS][Z|(+|-)hh[:]mm]` (the time portion of [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)). It defaults to `HH:mm:ss[.SSSSSS]` validation: no timezone offsets or `Z`, with arbitrary sub-second decimal.

The `z.string().datetime()` method enforces ISO 8601; default is no timezone offsets and arbitrary sub-second decimal precision.

Expand All @@ -915,18 +912,6 @@ datetime.parse("2020-01-01T00:00:00Z"); // pass
datetime.parse("2020-01-01T00:00:00.123Z"); // pass
datetime.parse("2020-01-01T00:00:00.123456Z"); // pass (arbitrary precision)
datetime.parse("2020-01-01T00:00:00+02:00"); // fail (no offsets allowed)

const date = z.string().date();

date.parse("2020-01-01"); // pass

const time = z.string().time();

time.parse("00:00:00"); // pass
time.parse("09:52:31"); // pass
time.parse("23:59:59.9999999"); // pass (arbitrary precision)
time.parse("00:00:00.123Z"); // fail (no `Z` allowed)
time.parse("00:00:00.123+02:00"); // fail (no offsets allowed)
```

Timezone offsets can be allowed by setting the `offset` option to `true`.
Expand All @@ -939,13 +924,6 @@ datetime.parse("2020-01-01T00:00:00.123+02:00"); // pass (millis optional)
datetime.parse("2020-01-01T00:00:00.123+0200"); // pass (millis optional)
datetime.parse("2020-01-01T00:00:00.123+02"); // pass (only offset hours)
datetime.parse("2020-01-01T00:00:00Z"); // pass (Z still supported)

const time = z.string().time({ offset: true });

time.parse("00:00:00+02:00"); // pass
time.parse("00:00:00.123+02:00"); // pass (millis optional)
time.parse("00:00:00.123+0200"); // pass (millis optional)
time.parse("00:00:00Z"); // pass (`Z` now supported)
```

You can additionally constrain the allowable `precision`. By default, arbitrary sub-second precision is supported (but optional).
Expand All @@ -964,6 +942,43 @@ time.parse("00:00:00"); // fail
time.parse("00:00:00.123456"); // fail
```

### Dates

The `z.string().date()` method validates strings in the format `YYYY-MM-DD`.

```ts
const date = z.string().date();

date.parse("2020-01-01"); // pass
date.parse("2020-1-1"); // fail
date.parse("2020-01-32"); // fail
```

### Times

The `z.string().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.

```ts
const time = z.string().time();

time.parse("00:00:00"); // pass
time.parse("09:52:31"); // pass
time.parse("23:59:59.9999999"); // pass (arbitrary precision)

time.parse("00:00:00.123Z"); // fail (no `Z` allowed)
time.parse("00:00:00.123+02:00"); // fail (no offsets allowed)
```

You can set the `precision` option to constrain the allowable decimal precision.

```ts
const time = z.string().time({ precision: 3 });

time.parse("00:00:00.123"); // pass
time.parse("00:00:00.123456"); // fail
time.parse("00:00:00"); // fail
```

### IP addresses

The `z.string().ip()` method by default validate IPv4 and IPv6.
Expand Down
74 changes: 45 additions & 29 deletions deno/lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@
- [Coercion for primitives](#coercion-for-primitives)
- [Literals](#literals)
- [Strings](#strings)
- [Datetime](#iso-datetimes)
- [IP](#ip-addresses)
- [Datetimes](#datetimes)
- [Dates](#dates)
- [Times](#times)
- [IP addresses](#ip-addresses)
- [Numbers](#numbers)
- [BigInts](#bigints)
- [NaNs](#nans)
Expand Down Expand Up @@ -638,6 +640,7 @@ There are a growing number of tools that are built atop or support Zod natively!
- [`zod_utilz`](https://github.com/JacobWeisenburger/zod_utilz): Framework agnostic utilities for Zod.
- [`zod-sandbox`](https://github.com/nereumelo/zod-sandbox): Controlled environment for testing zod schemas. [Live demo](https://zod-sandbox.vercel.app/).
- [`zod-dev`](https://github.com/schalkventer/zod-dev): Conditionally disables Zod runtime parsing in production.
- [`zod-accelerator`](https://github.com/duplojs/duplojs-zod-accelerator): Accelerates Zod's throughput up to ~100x.

## Installation

Expand Down Expand Up @@ -892,18 +895,13 @@ z.string().time({ message: "Invalid time string!" });
z.string().ip({ message: "Invalid IP address" });
```

### ISO Date, Time & Datetime validation
### Datetimes

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.
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.string().date()` method validates strings in the format `YYYY-MM-DD`.

The `z.string().time()` method validates strings in the format `HH:mm:ss[.SSSSSS][Z|(+|-)hh[:]mm]`
(the time portion of [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)). It defaults
to `HH:mm:ss[.SSSSSS]` validation: no timezone offsets or `Z`, with arbitrary sub-second
decimal.
The `z.string().time()` method validates strings in the format `HH:mm:ss[.SSSSSS][Z|(+|-)hh[:]mm]` (the time portion of [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)). It defaults to `HH:mm:ss[.SSSSSS]` validation: no timezone offsets or `Z`, with arbitrary sub-second decimal.

The `z.string().datetime()` method enforces ISO 8601; default is no timezone offsets and arbitrary sub-second decimal precision.

Expand All @@ -914,18 +912,6 @@ datetime.parse("2020-01-01T00:00:00Z"); // pass
datetime.parse("2020-01-01T00:00:00.123Z"); // pass
datetime.parse("2020-01-01T00:00:00.123456Z"); // pass (arbitrary precision)
datetime.parse("2020-01-01T00:00:00+02:00"); // fail (no offsets allowed)

const date = z.string().date();

date.parse("2020-01-01"); // pass

const time = z.string().time();

time.parse("00:00:00"); // pass
time.parse("09:52:31"); // pass
time.parse("23:59:59.9999999"); // pass (arbitrary precision)
time.parse("00:00:00.123Z"); // fail (no `Z` allowed)
time.parse("00:00:00.123+02:00"); // fail (no offsets allowed)
```

Timezone offsets can be allowed by setting the `offset` option to `true`.
Expand All @@ -938,13 +924,6 @@ datetime.parse("2020-01-01T00:00:00.123+02:00"); // pass (millis optional)
datetime.parse("2020-01-01T00:00:00.123+0200"); // pass (millis optional)
datetime.parse("2020-01-01T00:00:00.123+02"); // pass (only offset hours)
datetime.parse("2020-01-01T00:00:00Z"); // pass (Z still supported)

const time = z.string().time({ offset: true });

time.parse("00:00:00+02:00"); // pass
time.parse("00:00:00.123+02:00"); // pass (millis optional)
time.parse("00:00:00.123+0200"); // pass (millis optional)
time.parse("00:00:00Z"); // pass (`Z` now supported)
```

You can additionally constrain the allowable `precision`. By default, arbitrary sub-second precision is supported (but optional).
Expand All @@ -963,6 +942,43 @@ time.parse("00:00:00"); // fail
time.parse("00:00:00.123456"); // fail
```

### Dates

The `z.string().date()` method validates strings in the format `YYYY-MM-DD`.

```ts
const date = z.string().date();

date.parse("2020-01-01"); // pass
date.parse("2020-1-1"); // fail
date.parse("2020-01-32"); // fail
```

### Times

The `z.string().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.

```ts
const time = z.string().time();

time.parse("00:00:00"); // pass
time.parse("09:52:31"); // pass
time.parse("23:59:59.9999999"); // pass (arbitrary precision)

time.parse("00:00:00.123Z"); // fail (no `Z` allowed)
time.parse("00:00:00.123+02:00"); // fail (no offsets allowed)
```

You can set the `precision` option to constrain the allowable decimal precision.

```ts
const time = z.string().time({ precision: 3 });

time.parse("00:00:00.123"); // pass
time.parse("00:00:00.123456"); // fail
time.parse("00:00:00"); // fail
```

### IP addresses

The `z.string().ip()` method by default validate IPv4 and IPv6.
Expand Down
50 changes: 25 additions & 25 deletions deno/lib/__tests__/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,8 @@ test("nanoid", () => {
const nanoid = z.string().nanoid("custom error");
nanoid.parse("lfNZluvAxMkf7Q8C5H-QS");
nanoid.parse("mIU_4PJWikaU8fMbmkouz");
nanoid.parse("Hb9ZUtUa2JDm_dD-47EGv");
nanoid.parse("5Noocgv_8vQ9oPijj4ioQ");
nanoid.parse("Hb9ZUtUa2JDm_dD-47EGv");
nanoid.parse("5Noocgv_8vQ9oPijj4ioQ");
const result = nanoid.safeParse("Xq90uDyhddC53KsoASYJGX");
expect(result.success).toEqual(false);
if (!result.success) {
Expand Down Expand Up @@ -566,29 +566,29 @@ test("time parsing", () => {
expect(() => time2.parse("00:00:00.000")).toThrow();
expect(() => time2.parse("00:00:00.00+00:00")).toThrow();

const time3 = z.string().time({ offset: true });
time3.parse("00:00:00Z");
time3.parse("09:52:31Z");
time3.parse("00:00:00+00:00");
time3.parse("00:00:00+0000");
time3.parse("00:00:00.000Z");
time3.parse("00:00:00.000+00:00");
time3.parse("00:00:00.000+0000");
expect(() => time3.parse("")).toThrow();
expect(() => time3.parse("foo")).toThrow();
expect(() => time3.parse("00:00:00")).toThrow();
expect(() => time3.parse("00:00:00.000")).toThrow();

const time4 = z.string().time({ offset: true, precision: 0 });
time4.parse("00:00:00Z");
time4.parse("09:52:31Z");
time4.parse("00:00:00+00:00");
time4.parse("00:00:00+0000");
expect(() => time4.parse("")).toThrow();
expect(() => time4.parse("foo")).toThrow();
expect(() => time4.parse("00:00:00.0")).toThrow();
expect(() => time4.parse("00:00:00.000")).toThrow();
expect(() => time4.parse("00:00:00.000+00:00")).toThrow();
// const time3 = z.string().time({ offset: true });
// time3.parse("00:00:00Z");
// time3.parse("09:52:31Z");
// time3.parse("00:00:00+00:00");
// time3.parse("00:00:00+0000");
// time3.parse("00:00:00.000Z");
// time3.parse("00:00:00.000+00:00");
// time3.parse("00:00:00.000+0000");
// expect(() => time3.parse("")).toThrow();
// expect(() => time3.parse("foo")).toThrow();
// expect(() => time3.parse("00:00:00")).toThrow();
// expect(() => time3.parse("00:00:00.000")).toThrow();

// const time4 = z.string().time({ offset: true, precision: 0 });
// time4.parse("00:00:00Z");
// time4.parse("09:52:31Z");
// time4.parse("00:00:00+00:00");
// time4.parse("00:00:00+0000");
// expect(() => time4.parse("")).toThrow();
// expect(() => time4.parse("foo")).toThrow();
// expect(() => time4.parse("00:00:00.0")).toThrow();
// expect(() => time4.parse("00:00:00.000")).toThrow();
// expect(() => time4.parse("00:00:00.000+00:00")).toThrow();
});

test("IP validation", () => {
Expand Down
Loading
Loading