# Date picker

> A date field with a calendar that drops down, in the format you choose. A Pyzed component for Python desktop GUIs, with examples and its API.

Source: https://albakore.github.io/pyzed/library/date-picker/

:::tip[In plain words]
A box for a date. Type it, or press the little calendar and click a day.
:::

`ui.DatePicker` is a text field for one date. The calendar button (or Alt+↓) opens a [Calendar](https://albakore.github.io/pyzed/library/calendar/) under it, and the user can also type the date: it is read with `format` when they press Enter or leave the field. With the focus in the field, ↑ and ↓ move one day and Page Up/Page Down one month, without opening anything. `on_change` receives a `datetime.date` (or `None` when the field is cleared) in `event.value`.

`ui.DatePicker` · Category: [Dates and times](https://albakore.github.io/pyzed/library/#dates-and-times) · [Full reference →](https://albakore.github.io/pyzed/reference/ui/#datepicker)

**Code:**

```python
pz.container(
    ui.DatePicker(value="2026-10-22", format="DD/MM/YYYY", on_change=on_change),
    width=220, height=330, align_y="start")
```

:::note[New]
This component was added recently. Tell us how it works for you in the [issue tracker](https://github.com/albakore/pyzed/issues).
:::

## When to use

- For birthdays, due dates, bookings and any form field that holds a date.
- `format` takes tokens (`DD/MM/YYYY`, `MMM D, YYYY`, `dddd D MMMM`) or a `strftime` pattern (`%d.%m.%Y`). Month and day names follow the app's language.
- `min`, `max` and `disabled_dates` (dates, `"weekend"` or day names) turn days off.
- For two dates (from–to), use [Date range picker](https://albakore.github.io/pyzed/library/date-range-picker/).

## Usage

```python
import pyzed as pz
from pyzed import ui

import datetime

class Booking(pz.App):
    def __init__(self):
        self.day = datetime.date.today()

    def view(self):
        return pz.column([
            ui.field("Check-in", ui.DatePicker(value=self.day, format="ddd D MMM YYYY",
                                               min=datetime.date.today(), disabled_dates=["sunday"],
                                               on_change=lambda e: setattr(self, "day", e.value))),
            ui.label(f"Booked for {ui.format_date(self.day, 'dddd D MMMM')}"),
        ], spacing=12, width=320)
```

## Variants

:::note[About these captures]
Each capture is recorded by pyzed itself, off-screen at 2x, in One Dark or One Light to match this site's theme, and cropped to the component so it shows at its real size. The pointer hovers, presses or types into it; a component that does not react to the pointer cycles through a few themes. With reduced motion you see the first frame. Open **Code** under a capture to see the snippet that draws it; free names such as `on_save` stand for your own callbacks.
:::

### Format

Tokens: `YYYY` `YY`, `MMMM` `MMM` `MM` `M`, `DD` `D`, `dddd` `ddd`; `[text]` is copied as is.

```python
ui.DatePicker(value="2026-10-22", on_change=on_change)
```

```python
ui.DatePicker(value="2026-10-22", format="DD/MM/YYYY", on_change=on_change)
```

```python
ui.DatePicker(value="2026-10-22", format="MMM D, YYYY", on_change=on_change)
```

```python
ui.DatePicker(value="2026-10-22", format="ddd D MMM", on_change=on_change)
```

### States

```python
ui.DatePicker(placeholder="Pick a date", on_change=on_change)
```

```python
ui.DatePicker(value="2026-10-22", invalid=True, on_change=on_change)
```

```python
ui.DatePicker(value="2026-10-22", disabled=True, on_change=on_change)
```

```python
ui.DatePicker(value="2026-10-22", on_change=on_change)
```

## Examples

### Formats

`ui.format_date` and `ui.parse_date` use the same tokens as the field, for your own labels.

**Code:**

```python
pz.column([
    ui.label(ui.format_date("2026-10-22", "DD/MM/YYYY"), mono=True),
    ui.label(ui.format_date("2026-10-22", "MMM D, YYYY"), mono=True),
    ui.label(ui.format_date("2026-10-22", "dddd, D MMMM YYYY"), mono=True),
    ui.label(ui.format_date("2026-10-22", "[week of] D/M/YY"), mono=True),
], spacing=6)
```

## Events

Date picker has **4 events of its own**, listed below. Like every widget it also has the **18 common events** (`click`, `double_click`, `right_click`, `mouse_enter`, `drag`, `key_down`, `resize`…), attached with `.on(...)`: see [Events](https://albakore.github.io/pyzed/guides/events/) and [Events of every component](https://albakore.github.io/pyzed/library/events/).

It is a `pz.Component`, so it also raises `on_mount` and `on_unmount`, and its handlers get a `pz.Event` whose `value` holds the data.

| Event | Of | Handler gets | When |
| --- | --- | --- | --- |
| `on_change` | `ui.DatePicker` | `(event)` | Receives `event.value`: a `datetime.date` or `None`. |
| `on_open` | `ui.DatePicker` | `(event)` | The calendar opened. |
| `on_close` | `ui.DatePicker` | `(event)` | The calendar closed. |
| `on_dismiss` | `pz.popup` | `()` | Message or callable sent on a press outside or Escape. |

**Handler gets** is what Pyzed passes to your callable: `()` means nothing. Any handler can also be a message (a value that is not callable), which goes to `update()`.

### Handling the events

The same events written three ways. **Imperative**: methods of the app change `self`. **Declarative**: the handlers produce messages and one `update()` decides. **Functional**: a pure function draws an immutable state and asks for changes with `set_state(...)`. See [Imperative, declarative and functional](https://albakore.github.io/pyzed/guides/code-styles/).

**Imperative:**

```python
import datetime

class Booking(pz.App):
    def __init__(self):
        self.day = datetime.date(2026, 10, 22)

    def changed(self, event):            # on_change: event.value is a datetime.date (or None)
        self.day = event.value

    def view(self):
        return pz.column([
            ui.DatePicker(value=self.day, format="DD/MM/YYYY", on_change=self.changed),
            ui.label(ui.format_date(self.day, "dddd D MMMM") or "No date"),
        ], spacing=8, width=240)
```

**Declarative:**

```python
import datetime
from dataclasses import dataclass

@dataclass
class Picked:
    day: datetime.date

class Booking(pz.App):
    day = datetime.date(2026, 10, 22)

    def update(self, message):
        match message:
            case Picked(day):
                self.day = day

    def view(self):
        # DatePicker is a pz.Component: its handler gets a pz.Event
        return ui.DatePicker(value=self.day, on_change=lambda e: Picked(e.value))
```

**Functional:**

```python
import datetime
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    day: datetime.date = datetime.date(2026, 10, 22)

def view(state, set_state):
    return pz.column([
        ui.DatePicker(value=state.day, format="MMM D, YYYY", on_change=lambda e: set_state(day=e.value)),
        ui.label(f"Week {state.day.isocalendar().week}" if state.day else "No date"),
    ], spacing=8, width=240)
```

## Tips

:::tip
The calendar opens in a `pz.popup`, a layer drawn over the window: it is never clipped by a scrollable or a small container, flips above the field when there is no room below and closes with Escape or a click outside.
:::

## Properties and API

### `ui.DatePicker`

```python
class DatePicker
```

A component: a date field that opens a calendar below it, in a popup that panels never cut off. It can be typed in `format` (Enter or leaving the field reads it); once clicked, ↑/↓ move the date a day and Page Up/Down a month without opening anything, Alt+↓ opens the calendar and Escape closes it. See the [Date picker](https://albakore.github.io/pyzed/library/date-picker/) page.

| Property | Default | Description |
| --- | --- | --- |
| `value` | `None` | The date (`datetime.date`, ISO text or `None`). |
| `format` | `"YYYY-MM-DD"` | How it is written: `"DD/MM/YYYY"`, `"MMM D, YYYY"`... (see `format_date`). |
| `placeholder` | `""` | Hint while empty (default: the format). |
| `min` | `None` | The earliest date. |
| `max` | `None` | The latest date. |
| `disabled_dates` | `()` | Days that cannot be picked (as in `Calendar`). |
| `first_weekday` | `"monday"` | `"monday"`, `"sunday"` or `"saturday"`. |
| `week_numbers` | `False` | Shows week numbers in the calendar. |
| `clearable` | `True` | Shows an x that empties it. |
| `today_button` | `True` | Adds a Today link to the calendar. |
| `disabled` | `False` | Greys it out. |
| `invalid` | `False` | Paints the border in the error color. |

Events: `on_change`, `on_open`, `on_close` ([events](#events)).

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#datepicker)

### `ui.format_date`

```python
format_date(value, fmt='YYYY-MM-DD', *, language=None)
```

Writes a date with a pattern of tokens: `YYYY` 2026, `YY` 26, `MMMM` October, `MMM` Oct, `MM` 10, `M` 10, `DD` 05, `D` 5, `dddd` Monday, `ddd` Mon, `HH`/`hh` hours, `mm` minutes, `ss` seconds, `A` AM/PM and `[text]` kept as is. A pattern with `%` is a `strftime` one. Month and day names follow the language of `pyzed.i18n`.

| Property | Default | Description |
| --- | --- | --- |
| `value` | *required* | A date, a datetime, ISO text or `None` (gives `""`). |
| `fmt` | `"YYYY-MM-DD"` | The pattern. |
| `language` | `None` | A language for the names instead of the current one. |

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#format_date)

### `ui.parse_date`

```python
parse_date(text, fmt='YYYY-MM-DD', *, language=None)
```

Reads a date written with a pattern of `format_date` (month names in the current language or English); ISO `YYYY-MM-DD` always works. Returns `None` when the text is not a date.

| Property | Default | Description |
| --- | --- | --- |
| `text` | *required* | The text. |
| `fmt` | `"YYYY-MM-DD"` | The pattern. |
| `language` | `None` | The language of the names. |

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#parse_date)

### `pz.popup`

```python
popup(base, content=None, *, on_dismiss=None, side='bottom', align='start', gap=4.0)
```

Shows `content` over everything else next to `base`, like the menu of a drop-down: it is not cut off by the panels `base` sits in, flips to the other side when it does not fit and stays inside the window. A press outside it (and outside `base`) or Escape calls `on_dismiss`. With `content=None` only `base` is shown. The date pickers open their calendars with it.

| Property | Default | Description |
| --- | --- | --- |
| `base` | *required* | The widget it belongs to (a field, a button). |
| `content` | `None` | What opens (a panel, a menu), or `None` while closed. |
| `side` | `"bottom"` | `"bottom"`, `"top"`, `"right"` or `"left"` of `base`. |
| `align` | `"start"` | `"start"`, `"center"` or `"end"`: how it lines up with `base`. |
| `gap` | `4.0` | Space between `base` and the content. |

Events: `on_dismiss` ([events](#events)).

[Full reference →](https://albakore.github.io/pyzed/reference/widgets/#popup)

## Related

- [Calendar](https://albakore.github.io/pyzed/library/calendar/): A month grid to pick a day, a range or several days, always on screen.
- [Date range picker](https://albakore.github.io/pyzed/library/date-range-picker/): Two dates in one field, with two months side by side and quick ranges.
- [Time picker](https://albakore.github.io/pyzed/library/time-picker/): A time field with columns of hours and minutes, in 24 or 12 hours.
- [Input](https://albakore.github.io/pyzed/library/input/): A controlled text field with icon, placeholder and password mode.
