Skip to content
IzzyBlazor 0.1.0-alpha.20

Enter all results ↑ ↓ move Esc close

DateField

Since 0.1.0-alpha.20
A date to type or choose from a calendar, or a date and time.

Static server-side rendering: no circuit, no WebAssembly.

A date field, or a date and time field.

Usage

razor
<DateField @bind-Value="Model.Due" />
<DateField @bind-Value="Model.From" Max="@DateOnly.FromDateTime(DateTime.UtcNow)" />
<DateField @bind-Value="Model.Due" DateDisabled="d => d.DayOfWeek == DayOfWeek.Sunday" />
<DateField @bind-Value="Model.Due" UseNative />
<DateField @bind-Value="Model.StartsAt" TimeZone="@zone" />

Limits from the model

Min and Max come from [Range], or set them on the field, as Check-out's Min follows Check-in.

Preview

Bookings open for 2027.

Code

Limits.razor
@using System.ComponentModel.DataAnnotations

<Form Model="Model" FormName="date-field-limits" OnValidSubmit="Save" MaxWidth="20.Rem" Grow>
    <DateField @bind-Value="Model!.CheckIn" Hint="Bookings open for 2027." />
    <DateField @bind-Value="Model!.CheckOut" Min="Model!.CheckIn" />
    <Row><Button Submit>Book</Button></Row>
    @if (_saved is { } saved)
    {
        <Alert Status="Status.Success">Booked @saved.In.ToString("MMM d") to @(saved.Out?.ToString("MMM d") ?? "open").</Alert>
    }
</Form>

@code {
    [SupplyParameterFromForm(FormName = "date-field-limits")]
    private Booking? Model { get; set; }

    // What was saved, not the fields: on an interactive page they follow typing.
    private (DateOnly In, DateOnly? Out)? _saved;

    protected override void OnInitialized() => Model ??= new();

    private void Save() => _saved = (Model!.CheckIn!.Value, Model.CheckOut);

    public sealed class Booking
    {
        [Required, Range(typeof(DateOnly), "2027-01-01", "2027-12-31", ErrorMessage = "Pick a date in 2027.")]
        [Display(Name = "Check-in")]
        public DateOnly? CheckIn { get; set; } = new DateOnly(2027, 6, 14);

        [Display(Name = "Check-out")]
        public DateOnly? CheckOut { get; set; }
    }
}

Static: The browser stops a check-in outside 2027, in the model's words.

Time zones

Use TimeZone for the zone a time is typed in; without it, UTC. A DateTime comes back in UTC, a DateTimeOffset with the zone's offset.

Preview

Code

TimeZones.razor
<Form Model="Model" FormName="date-field-time-zones" OnValidSubmit="Save" MaxWidth="22.Rem" Grow>
    <DateField @bind-Value="Model!.Deploy" Label="Deploy at (UTC)" />
    <DateField @bind-Value="Model!.Standup" Label="Standup (New York)" TimeZone="NewYork" />
    <Row><Button Submit>Schedule</Button></Row>
    @if (_saved is { } saved)
    {
        <Alert Status="Status.Success">Saved @saved.Deploy.ToString("u") and @saved.Standup.ToString("yyyy-MM-dd HH:mm zzz").</Alert>
    }
</Form>

@code {
    [SupplyParameterFromForm(FormName = "date-field-time-zones")]
    private Schedule? Model { get; set; }

    private static readonly TimeZoneInfo NewYork = TimeZoneInfo.FindSystemTimeZoneById("America/New_York");

    private (DateTime Deploy, DateTimeOffset Standup)? _saved;

    protected override void OnInitialized() => Model ??= new();

    private void Save() => _saved = (Model!.Deploy, Model.Standup);

    public sealed class Schedule
    {
        // A DateTime comes back in UTC; a DateTimeOffset with the zone's offset.
        public DateTime Deploy { get; set; } = new(2027, 3, 1, 22, 0, 0, DateTimeKind.Utc);

        public DateTimeOffset Standup { get; set; } = new(2027, 3, 2, 9, 30, 0, TimeSpan.FromHours(-5));
    }
}

Static: The script posts the New York time with its offset, so the saved value is right.

A month or a year

Type takes a month, held as the 1st of it, or a year, held in an int. Min and Max compare whole months or years.

Preview

Code

Periods.razor
<Form Model="Model" FormName="date-field-periods" OnValidSubmit="Save" MaxWidth="20.Rem" Grow>
    <DateField @bind-Value="Model!.Starts" Type="DateFieldType.Month" Min="@First" Clearable />
    <DateField @bind-Value="Model!.Year" Type="DateFieldType.Year" Label="Model year" Min="2000" Max="2030" />
    <Row><Button Submit>Save</Button></Row>
    @if (_saved is { } saved)
    {
        <Alert Status="Status.Success">Starts @(saved.Starts?.ToString("MMMM yyyy") ?? "unset"), model year @(saved.Year?.ToString() ?? "unset").</Alert>
    }
</Form>

@code {
    private static readonly DateOnly First = new(2027, 1, 1);

    [SupplyParameterFromForm(FormName = "date-field-periods")]
    private Plan? Model { get; set; }

    // What was saved, not the fields: on an interactive page they follow what's chosen.
    private (DateOnly? Starts, int? Year)? _saved;

    protected override void OnInitialized() => Model ??= new();

    private void Save() => _saved = (Model!.Starts, Model.Year);

    public sealed class Plan
    {
        public DateOnly? Starts { get; set; } = new DateOnly(2027, 3, 1);

        public int? Year { get; set; }
    }
}

Static: On a desktop, Starts is chosen, not typed. On a phone it's the browser's month input.

Disabled days and the browser's calendar

Use DateDisabled to turn days off, which the server checks too, and UseNative to keep the browser's calendar.

Preview

We don't deliver at weekends.

The browser's calendar.

Code

Days.razor
<Form Model="Model" FormName="date-field-days" OnValidSubmit="Save" MaxWidth="20.Rem" Grow>
    <DateField @bind-Value="Model!.Delivery" Hint="We don't deliver at weekends." DateDisabled="IsWeekend" Min="@First" Max="@Last" Clearable />
    <DateField @bind-Value="Model!.Reminder" UseNative Hint="The browser's calendar." />
    <Row><Button Submit>Save</Button></Row>
    @if (_saved is { } saved)
    {
        <Alert Status="Status.Success">Delivery on @saved.ToString("MMM d, yyyy").</Alert>
    }
</Form>

@code {
    private static readonly DateOnly First = new(2027, 3, 1);
    private static readonly DateOnly Last = new(2027, 5, 31);

    [SupplyParameterFromForm(FormName = "date-field-days")]
    private Order? Model { get; set; }

    private DateOnly? _saved;

    protected override void OnInitialized() => Model ??= new();

    private void Save() => _saved = Model!.Delivery;

    private static bool IsWeekend(DateOnly day) => day.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday;

    public sealed class Order
    {
        public DateOnly? Delivery { get; set; } = new DateOnly(2027, 3, 15);

        public DateOnly? Reminder { get; set; }
    }
}

Static: On a phone Reminder opens the browser's calendar and Delivery this one, as a browser's can't show which days are off.

Sizes, read-only and disabled

Preview

Read-only: posted, but can't be changed.

Disabled: not posted.

Code

States.razor
<Form Model="Model" FormName="date-field-states" MaxWidth="20.Rem" Grow>
    <DateField @bind-Value="Model!.Small" Label="Small" Size="ControlSize.Sm" />
    <DateField @bind-Value="Model!.Large" Label="Large" Size="ControlSize.Lg" />
    <DateField @bind-Value="Model!.Signed" Label="Signed on" ReadOnly Hint="Read-only: posted, but can't be changed." />
    <DateField @bind-Value="Model!.Renews" Label="Renews on" Disabled Hint="Disabled: not posted." />
</Form>

@code {
    [SupplyParameterFromForm(FormName = "date-field-states")]
    private Contract? Model { get; set; }

    protected override void OnInitialized() => Model ??= new();

    public sealed class Contract
    {
        public DateOnly Small { get; set; } = new(2027, 1, 4);
        public DateOnly Large { get; set; } = new(2027, 1, 4);
        public DateOnly Signed { get; set; } = new(2026, 9, 30);
        public DateOnly Renews { get; set; } = new(2027, 9, 30);
    }
}

Details

The calendar writes to the input, so posting and binding don't change. UseNative keeps the browser's calendar. So do no script and a phone, unless the field has DateDisabled, which a browser's calendar can't show: there it's the library's, and on a phone the input can't be typed in. Min, Max and DateDisabled are checked again on the server, so a typed date is turned away when the form posts.

With a time, the calendar has a box each for the hour and minute, with seconds if Step has them and AM or PM if the culture does, and OK: nothing is written until OK, and closing it any other way, by Escape or a click outside, drops the choice. Min and Max stop the days, and their times are checked as the form posts.

A DateOnly is a date. A DateTime or DateTimeOffset takes a date and time, or only a date with Type="DateFieldType.Date" or [DataType(DataType.Date)] on the property (midnight in TimeZone).

Type="DateFieldType.Month" takes a month, held as the 1st of it in a DateOnly or DateTime, and Type="DateFieldType.Year" a year, held in an int. Each has a page of twelve to choose from. Min and Max compare whole months or years, so a Min of March 15 allows March.

razor
<DateField @bind-Value="Model.Expires" Type="DateFieldType.Month" />
<DateField @bind-Value="Model.Year" Type="DateFieldType.Year" Min="2000" Max="2030" />
On a desktop a month is chosen, not typed, and its button shows it, since browsers other than
Chromium's have no month input. A phone and UseNative keep the browser's input, and with no script
it's the browser's too, a text box with a pattern where it has none. A year is a number input
everywhere, with the page beside it on a desktop. [Range] compares the 1st of the month, so give
it a 1st, as a Range starting on March 15 would turn away March.
DateDisabled, DayClass, ShowWeekNumbers, FirstDayOfWeek, Step, TimeZone and a DateTimeOffset
throw with these, as they're about days and times.

TimeZone is the zone the entered time is in, UTC unless set. A DateTimeOffset comes back with that zone's offset, and a DateTime comes back in UTC; a DateTime that isn't Local is read as UTC. On an interactive page its Kind is Utc. After a static post Blazor's form binding sets it, as Unspecified (the time is still UTC): use DateTime.SpecifyKind before ToUniversalTime or a timestamptz column, or bind a DateTimeOffset. On a server, TimeZoneInfo.Local is the server's zone, not the user's: ask the user for theirs, or keep UTC and say so in the label, as in "From (UTC)". A time a change of the clocks skips, such as 02:30, is taken with the zone's standard offset, so it's an hour later. The calendar marks today in TimeZone if it's set, and otherwise on the person's clock.

Mode support

What works in each mode, DateField
FeatureStaticInteractive
A calendar to choose a date from, or the browser's with UseNativeYesYes
Min, Max and Step checked before posting, and Min, Max and DateDisabled checked again on the serverYesYes
Binds DateOnly, DateTime or DateTimeOffsetYesYes
A month or a year to choose from a page of twelveYesYes
Times read in TimeZone (UTC unless set)YesYes

Static: static server-side rendering, with no circuit and no WebAssembly. Interactive: Server, WebAssembly or Auto.

Without JavaScript

Every page starts as plain HTML: before IzzyBlazor's script loads on a static page, while an interactive page prerenders, and with JavaScript off. With JavaScript off, the browser's own calendar opens in place of the library's, and a posted DateTime or DateTimeOffset isn't read in TimeZone: Blazor reads a DateTimeOffset in the server's time zone and a DateTime as UTC. A DateOnly isn't affected, nor is a TimeZone of UTC on a server that runs in UTC.

Accessibility

  • It renders the browser's own date or date-and-time input with a label linked to it: screen readers read each part (day, month, year) and arrow keys change it.
  • Label, hint and errors work as in TextField.
  • The time zone isn't announced: put it in the label when it matters, as in "Starts (UTC)", since add-on text isn't part of the field's name.
  • The calendar's button is named "Choose date" (ChooseLabel), or "Change date" and the date once there is one. Its panel is a non-modal dialog, named by the button, with the month as in Calendar. Focus goes to the chosen date or today, and on closing back to where it opened from.
  • A month's button stands in for its input on a desktop, so it's named by the field's label and the month, and takes the hint and errors. A page of months or years is a grid, named by its year or its span, with the chosen one's aria-selected and this one's aria-current.

Keyboard navigation

In the date field

DateField keys, In the date field
Press this keyTo do this
Up or DownChange the focused part of the date
Left or RightMove between the parts of the date
Alt+DownOpen the calendar

In the calendar's days

DateField keys, In the calendar's days
Press this keyTo do this
Left or RightMove a day (reversed in right-to-left)
Up or DownMove a week
Home or EndGo to the start or end of the week
Page Up or Page DownMove a month, keeping the day number or the month's last day
Shift+Page Up or Shift+Page DownMove a year
Enter or SpaceChoose the date and close; with a time, choose the date and stay open

In the calendar's time

DateField keys, In the calendar's time
Press this keyTo do this
EnterSave the date and time, and close

In a page of months or years

DateField keys, In a page of months or years
Press this keyTo do this
Left or RightMove one (reversed in right-to-left)
Up or DownMove a row
Home or EndGo to the start or end of the row
Page Up or Page DownMove twelve: a year of months, or twelve years
Enter or SpaceChoose it and close

In the calendar

DateField keys, In the calendar
Press this keyTo do this
Tab or Shift+TabMove through the header, the days and the buttons, and close past either end
EscapeClose without changing the value

Parameters

DateField parameters
PropTypeDefault
ReadOnlyMakes the field read-only: it can be focused, and it still posts.boolfalse
TypeA date, or a date and time, a month or a year.DateFieldType?computed
MinThe earliest value.TValuecomputed
MaxThe latest value.TValuecomputed
StepThe step between allowed values: whole days for a date (1 day unless set), seconds for a date and time (1 minute unless set).TimeSpan?
TimeZoneThe zone the entered time is in, for a DateTime or DateTimeOffset.TimeZoneInfo?computed
StartAddonContent attached before the control, outside its border.RenderFragment?
EndAddonContent attached after the control, outside its border, such as the time zone.RenderFragment?
UseNativeKeeps the browser's calendar in place of the library's.boolfalse
ClearableA button in the field that empties it, for a nullable value, there while it holds something.boolfalse
ChooseLabelThe name of the calendar's button, in place of "Choose date" and "Change date" with the date.string?
FirstDayOfWeekThe first column.DayOfWeek?computed
CultureThe culture for the month and day names, the first day and the week numbers.CultureInfo?computed
ShowWeekNumbersAdds a first column with the week of the year, counted as the culture counts them.boolfalse
ShowAdjacentDaysWhether to show the days of the months before and after, muted, in the first and last weeks.booltrue
DateDisabledWhether a day can't be chosen.Func<DateOnly, bool>?
DateDisabledWindowHow far each way from the value or today to call DateDisabled and DayClass, in whole days, for an end the limits don't set.TimeSpan?
DayClassA class for a day's button, or null.Func<DateOnly, string?>?
@bind-ValueThe value, bound with @bind-Value.TValue
DisplayNameThe field's name in validation messages from the input itself, such as a value that can't be parsed.string?

See also

  • TimeField: a time of day, in the browser's own input, with a list of times to choose from.
  • TextField: a labeled text input with add-ons and a password reveal.
  • NumberField: a number input with stepper buttons.
  • Form: posts on static pages, validates live on interactive ones.

Connection lost

Rejoining the server…