Sitelet https://github.com/angular/angular/commit/276248ff3d93caa3d170edda90a0b9ee706ad7ad
Skip to content

Commit 276248f

Browse files
jhjh1214crisbeto
authored andcommitted
docs(forms): document that minDate and maxDate expect UTC dates
A date input reads and writes `Date` values at midnight UTC, which is how `valueAsDate` behaves. `minDate` and `maxDate` compare against that value and format the `min` and `max` attributes with UTC getters, so a limit built from local time (`new Date(2026, 5, 1)` or `new Date()`) is shifted by the user's UTC offset. Depending on the time zone, the limit date itself is rejected or the attribute is a day off. Add a `minDate() and maxDate()` section to the validation guide, which had none, and note the UTC contract in the JSDoc of `minDate` and `maxDate`. Point the `@see` links of those two functions at the new section. (cherry picked from commit 2a66275)
1 parent d04f214 commit 276248f

3 files changed

Lines changed: 55 additions & 2 deletions

File tree

‎adev/src/content/guide/forms/signals/validation.md‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,49 @@ ageForm = form(this.ageModel, (schemaPath) => {
224224
});
225225
```
226226

227+
### minDate() and maxDate()
228+
229+
The `minDate()` and `maxDate()` validation rules work with `Date` values, such as a field bound to `<input type="date">`. They also set the `min` and `max` attributes of that input.
230+
231+
```angular-ts
232+
import {Component, signal} from '@angular/core';
233+
import {form, FormField, minDate, maxDate} from '@angular/forms/signals';
234+
235+
@Component({
236+
selector: 'app-booking-form',
237+
imports: [FormField],
238+
template: `
239+
<form novalidate>
240+
<label>
241+
Check-in
242+
<input type="date" [formField]="bookingForm.checkIn" />
243+
</label>
244+
</form>
245+
`,
246+
})
247+
export class BookingFormComponent {
248+
bookingModel = signal<{checkIn: Date | null}>({checkIn: null});
249+
250+
bookingForm = form(this.bookingModel, (schemaPath) => {
251+
minDate(schemaPath.checkIn, new Date('2026-06-01'), {message: 'Bookings open on June 1'});
252+
maxDate(schemaPath.checkIn, new Date('2026-08-31'), {message: 'Bookings close on August 31'});
253+
});
254+
}
255+
```
256+
257+
IMPORTANT: An `<input type="date">` reads and writes `Date` values at midnight UTC, whatever the user's time zone, because that is how the browser's `valueAsDate` works. Build the limits the same way, for example with `new Date('2026-06-01')` or `new Date(Date.UTC(2026, 5, 1))`. A `Date` created from local time, such as `new Date(2026, 5, 1)` or `new Date()`, is shifted by the user's UTC offset. Depending on the time zone, the `min` or `max` attribute can then be off by a day, and the limit date itself can be rejected.
258+
259+
To require a date that is not before today, use today's calendar date in the user's time zone, expressed at midnight UTC:
260+
261+
```ts
262+
const now = new Date();
263+
const today = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()));
264+
265+
minDate(schemaPath.checkIn, today, {message: 'Check-in cannot be in the past'});
266+
```
267+
268+
Passing `new Date()` directly can reject today's date, because the value picked in the input is midnight UTC, which is earlier than the current time. This happens in UTC and in the time zones behind it.
269+
227270
### minLength() and maxLength()
228271

229272
The `minLength()` and `maxLength()` validation rules work with strings and arrays:

‎packages/forms/signals/src/api/rules/validation/max_date.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@ import {maxDateError} from './validation_errors';
1818
* This function can only be called on date paths.
1919
* In addition to binding a validator, this function adds `MAX` property to the field.
2020
*
21+
* When the field is bound to an `<input type="date">`, its value is a `Date` at midnight UTC, so the
22+
* maximum should be built the same way, e.g. `new Date('2026-08-31')` or
23+
* `new Date(Date.UTC(2026, 7, 31))`. A `Date` created from local time, such as `new Date(2026, 7, 31)`
24+
* or `new Date()`, is offset by the user's time zone, which can make the limit date itself invalid.
25+
*
2126
* @param path Path of the field to validate
2227
* @param maxDate The maximum date, or a LogicFn that returns the maximum date.
2328
* @param config Optional, allows providing any of the following options:
2429
* - `error`: Custom validation error(s) to be used instead of the default `maxDateError()`
2530
* or a function that receives the `FieldContext` and returns custom validation error(s).
2631
* @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array)
2732
*
28-
* @see [Signal Form Max Validation](guide/forms/signals/validation#min-and-max)
33+
* @see [Signal Form Max Validation](guide/forms/signals/validation#mindate-and-maxdate)
2934
* @category validation
3035
* @publicApi 22.0
3136
*/

‎packages/forms/signals/src/api/rules/validation/min_date.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@ import {minDateError} from './validation_errors';
1818
* This function can only be called on date paths.
1919
* In addition to binding a validator, this function adds `MIN` property to the field.
2020
*
21+
* When the field is bound to an `<input type="date">`, its value is a `Date` at midnight UTC, so the
22+
* minimum should be built the same way, e.g. `new Date('2026-06-01')` or
23+
* `new Date(Date.UTC(2026, 5, 1))`. A `Date` created from local time, such as `new Date(2026, 5, 1)`
24+
* or `new Date()`, is offset by the user's time zone, which can make the limit date itself invalid.
25+
*
2126
* @param path Path of the field to validate
2227
* @param minDate The minimum date, or a LogicFn that returns the minimum date.
2328
* @param config Optional, allows providing any of the following options:
2429
* - `error`: Custom validation error(s) to be used instead of the default `minDateError()`
2530
* or a function that receives the `FieldContext` and returns custom validation error(s).
2631
* @template TPathKind The kind of path the logic is bound to (a root path, child path, or item of an array)
2732
*
28-
* @see [Signal Form Min Validation](guide/forms/signals/validation#min-and-max)
33+
* @see [Signal Form Min Validation](guide/forms/signals/validation#mindate-and-maxdate)
2934
* @category validation
3035
* @publicApi 22.0
3136
*/

0 commit comments

Comments
 (0)