# Date Range Picker

Date range pickers allow users to select a range dates and times from a pop up calendar.

## Example

```python
from deephaven import ui

my_date_range_picker_basic = ui.date_range_picker(label="Date range")
```

## Date types

A date range picker can be used to select a `range` of dates.

The `range` is a dictionary with a `start` date and an `end` date. e.g. `{ "start": "2024-01-02", "end": "2024-01-05" }`

The date range picker accepts the following date types as inputs:

- `None`
- `LocalDate`
- `ZoneDateTime`
- `Instant`
- `int`
- `str`
- `datetime.datetime`
- `numpy.datetime64`
- `pandas.Timestamp`

The `start` and `end` dates should be the same type.

The input will be converted to one of three Java date types:

1. `LocalDate`: in the ISO-8601 system, a LocalDate is a date without a time zone, such as “2007-12-03” or “2057-01-28”.
   This will create a date range picker with a granularity of days.
2. `Instant`: An Instant represents an unambiguous specific point on the timeline, such as 2021-04-12T14:13:07 UTC.
   This will create a date range picker with a granularity of seconds in UTC. The time zone will be rendered as the time zone in user settings.
3. `ZonedDateTime`: A ZonedDateTime represents an unambiguous specific point on the timeline with an associated time zone, such as 2021-04-12T14:13:07 America/New_York.
   This will create a date range picker with a granularity of seconds in the specified time zone. The time zone will be rendered as the specified time zone.

The `start` and `end` inputs are converted according to the following rules:

1. If the input is one of the three Java date types, use that type.
2. A date string such as “2007-12-03” will parse to a `LocalDate`.
3. A string with a date, time, and time zone such as “2021-04-12T14:13:07 America/New_York” will parse to a `ZonedDateTime`
4. All other types will attempt to convert in this order: `Instant`, `ZonedDateTime`, `LocalDate`

The format of the date range picker and the type of the value passed to the `on_change` handler
are determined by the type of the following props in order of precedence:

1. `value`
2. `default_value`
3. `placeholder_value`

If none of these are provided, the `on_change` handler will be passed a range of `Instant`.

```python
from deephaven import ui
from deephaven.time import to_j_local_date, dh_today, to_j_instant, to_j_zdt


@ui.component
def date_range_picker_example(start, end):
    dates, set_dates = ui.use_state({"start": start, "end": end})
    return [ui.date_range_picker(on_change=set_dates, value=dates), ui.text(str(dates))]


zdt_start = to_j_zdt("1995-03-22T11:11:11.23142 America/New_York")
zdt_end = to_j_zdt("1995-03-25T11:11:11.23142 America/New_York")
instant_start = to_j_instant("2022-01-01T00:00:00 ET")
instant_end = to_j_instant("2022-01-05T00:00:00 ET")
local_start = to_j_local_date("2024-05-06")
local_end = to_j_local_date("2024-05-10")

my_zoned_example = date_range_picker_example(zdt_start, zdt_end)
my_instant_example = date_range_picker_example(instant_start, instant_end)
my_local_example = date_range_picker_example(local_start, local_end)
```

## Value

A date range picker displays a `placeholder` by default. An initial, uncontrolled value can be provided to the date range picker using the `defaultValue` prop. Alternatively, a controlled value can be provided using the `value` prop.

```python
from deephaven import ui


@ui.component
def example():
    value, set_value = ui.use_state({"start": "2020-02-03", "end": "2020-02-08"})
    return ui.flex(
        ui.date_range_picker(
            label="Date range (uncontrolled)",
            default_value={"start": "2020-02-03", "end": "2020-02-08"},
        ),
        ui.date_range_picker(
            label="Date range (controlled)", value=value, on_change=set_value
        ),
        gap="size-150",
        wrap=True,
    )


my_example = example()
```

## Time zones

Date range picker is time zone aware when `ZonedDateTime` or `Instant` objects are provided as the value. In this case, the time zone abbreviation is displayed, and time zone concerns such as daylight saving time are taken into account when the value is manipulated.

In most cases, your data will come from and be sent to a server as an `ISO 8601` formatted string.

For `ZonedDateTime` objects, the date range picker will display the specified time zone.

For `Instant` objects, the date range picker will display the time zone from the user settings.

```python
from deephaven import ui
from deephaven.time import to_j_instant

my_zoned_date_time = ui.date_range_picker(
    label="Date range",
    default_value={
        "start": "2022-11-07T00:45 America/Los_Angeles",
        "end": "2022-11-08T11:15 America/Los_Angeles",
    },
)

my_instant = ui.date_range_picker(
    label="Date range",
    default_value={
        "start": to_j_instant("2022-11-07T00:45Z"),
        "end": to_j_instant("2022-11-08T11:15Z"),
    },
)
```

## Granularity

The `granularity` prop allows you to control the smallest unit that is displayed by a date range picker. By default, `LocalDate` values are displayed with “DAY” granularity (year, month, and day), and `ZonedDateTime` and `Instant` values are displayed with “SECOND” granularity.

In addition, when a value with a time is provided but you wish to display only the date, you can set the granularity to “DAY”. This has no effect on the actual value (it still has a time component), only on what fields are displayed. In the following example, two date range pickers are synchronized with the same value but display different granularities.

```python
from deephaven import ui


@ui.component
def granularity_example():
    value, set_value = ui.use_state(
        {"start": "2021-04-07T18:45:22 UTC", "end": "2021-04-08T20:00:00 UTC"}
    )
    return ui.flex(
        ui.date_range_picker(
            label="Date range and time range",
            granularity="SECOND",
            value=value,
            on_change=set_value,
        ),
        ui.date_range_picker(
            label="Date range", granularity="DAY", value=value, on_change=set_value
        ),
        gap="size-150",
        wrap=True,
    )


my_granularity_example = granularity_example()
```

## HTML forms

Date range picker supports the `start_name` and `end_name` props for integration with HTML forms. The values will be submitted to the server as `ISO 8601` formatted strings according to the granularity of the value. For example, if the date range picker allows selecting only dates, then strings such as “2023-02-03” will be submitted, and if it allows selecting times, then strings such as “2023-02-03T08:45:00”

```python
from deephaven import ui

my_date_range_picker_forms = ui.form(
    ui.date_range_picker(
        label="Trip dates", start_name="startDate", end_name="endDate"
    ),
    ui.button("Submit", type="submit"),
    on_submit=print,
)
```

## Labeling

A visual label should be provided for the date range picker using the `label` prop. If the date range picker is required, the `is_required` and `necessity_indicator` props can be used to show a required state.

```python
from deephaven import ui

my_date_range_picker_labeling = ui.flex(
    ui.date_range_picker(label="Date range"),
    ui.date_range_picker(
        label="Date range", is_required=True, necessity_indicator="icon"
    ),
    ui.date_range_picker(
        label="Date range", is_required=True, necessity_indicator="label"
    ),
    ui.date_range_picker(label="Date range", necessity_indicator="label"),
)
```

## Events

Date range pickers support selection through mouse, keyboard, and touch inputs via the `on_change` prop, which receives the value as an argument.

```python
from deephaven import ui


@ui.component
def event_example():
    value, set_value = ui.use_state({"start": "2020-02-03", "end": "2020-02-08"})
    return ui.date_range_picker(
        label="Date range (controlled)", value=value, on_change=set_value
    )


my_event_example = event_example()
```

## Validation

The `is_required` prop ensures that the user selects a date range. The related `validation_behaviour` prop allows the user to specify aria or native verification.

When the prop is set to “native”, the validation errors block form submission and are displayed as help text automatically.

```python
from deephaven import ui


@ui.component
def date_range_picker_validation_behaviour_example():
    return ui.form(
        ui.date_range_picker(
            validation_behavior="native",
            is_required=True,
        )
    )


my_date_range_picker_validation_behaviour_example = (
    date_range_picker_validation_behaviour_example()
)
```

## Minimum and maximum values

The `min_value` and `max_value` props can also be used to ensure the value is within a specific range. Date range picker also validates that the end date is after the start date.

```python
from deephaven import ui

my_date_range_picker_basic = ui.date_range_picker(
    label="Date range",
    min_value="2024-01-01",
    default_value={"start": "2022-02-03", "end": "2022-05-03"},
)
```

## Label position

By default, the position of a date range picker’s label is above the date range picker, but it can be moved to the side using the `label_position` prop.

```python
from deephaven import ui


@ui.component
def date_range_picker_label_position_examples():
    return [
        ui.date_range_picker(
            label="Test Label",
        ),
        ui.date_range_picker(
            label="Test Label",
            label_position="side",
        ),
    ]


my_date_range_picker_label_position_examples = (
    date_range_picker_label_position_examples()
)
```

## Quiet state

The `is_quiet` prop makes a date range picker “quiet”. This can be useful when the picker and its corresponding styling should not distract users from surrounding content.

```python
from deephaven import ui


my_date_range_picker_is_quiet_example = ui.date_range_picker(
    is_quiet=True,
)
```

## Disabled state

The `is_disabled` prop disables a picker to prevent user interaction. This is useful when the date range picker should be visible but not available for selection.

```python
from deephaven import ui


my_date_range_picker_is_disabled_example = ui.date_range_picker(
    is_disabled=True,
)
```

## Help text

A date range picker can have both a `description` and an `error_message`. Use the error message to offer specific guidance on how to correct the input.

The `validation_state` prop can be used to set whether the current date range picker state is `valid` or `invalid`.

```python
from deephaven import ui


@ui.component
def date_range_picker_help_text_examples():
    return [
        ui.date_range_picker(
            label="Sample Label",
            description="Enter a date range.",
        ),
        ui.date_range_picker(
            label="Sample Label",
            validation_state="valid",
            error_message="Sample invalid error message.",
        ),
        ui.date_range_picker(
            label="Sample Label",
            validation_state="invalid",
            error_message="Sample invalid error message.",
        ),
    ]


my_date_range_picker_help_text_examples = date_range_picker_help_text_examples()
```

## Contextual help

Using the `contextual_help` prop, a `ui.contextual_help` can be placed next to the label to provide additional information about the date range picker.

```python
from deephaven import ui


date_range_picker_contextual_help_example = ui.date_range_picker(
    label="Sample Label",
    contextual_help=ui.contextual_help(ui.heading("Content tips")),
)
```

## Custom width

The `width` prop adjusts the width of a date range picker, and the `max_width` prop enforces a maximum width.

```python
from deephaven import ui


@ui.component
def date_range_picker_width_examples():
    return [
        ui.date_range_picker(
            width="size-6000",
        ),
        ui.date_range_picker(
            width="size-6000",
            max_width="100%",
        ),
    ]


my_date_range_picker_width_examples = date_range_picker_width_examples()
```

## Maximum visible months

By default, the calendar popover displays a single month. The `max_visible_months` prop allows displaying up to 3 months at a time, if screen space permits.

```python
from deephaven import ui


date_range_picker_months_example = ui.date_range_picker(
    label="Date range", max_visible_months=3
)
```

## Page behavior

By default, when pressing the next or previous buttons, pagination advances by the `max_visible_months` value. This behavior can be changed to page by single months by setting `page_behavior` to single.

```python
from deephaven import ui


date_range_picker_page_example = ui.date_range_picker(
    label="Date range", max_visible_months=3, page_behavior="single"
)
```

## Hide time zone

The time zone can be hidden using the `hide_time_zone` option.

```python
from deephaven import ui

my_hide_time_zone_example = ui.date_range_picker(
    label="Date range",
    default_value={
        "start": "2022-11-07T00:45 America/Los_Angeles",
        "end": "2022-11-08T11:15 America/Los_Angeles",
    },
    hide_time_zone=True,
)
```

## Hour cycle

By default, date range picker displays times in either a `12` or `24` hour format depending on the user’s locale. However, this can be overridden using the `hour_cycle` prop.

```python
from deephaven import ui


date_range_picker_hour_cycle_example = ui.date_range_picker(
    label="Date range", hour_cycle=24
)
```

## Time table filtering

Date range pickers can be used to filter tables with time columns.

```python
from deephaven.time import dh_now
from deephaven import time_table, ui


@ui.component
def date_table_filter(table, start_date, end_date, time_col="Timestamp"):
    dates, set_dates = ui.use_state({"start": start_date, "end": end_date})
    start = dates["start"]
    end = dates["end"]
    return [
        ui.date_range_picker(label="Dates", value=dates, on_change=set_dates),
        table.where(f"{time_col} >= start && {time_col} < end"),
    ]


SECONDS_IN_DAY = 86400
today = dh_now()
_table = time_table("PT1s").update_view(
    ["Timestamp=today.plusSeconds(SECONDS_IN_DAY*i)", "Row=i"]
)
date_filter = date_table_filter(_table, today, today.plusSeconds(SECONDS_IN_DAY * 10))
```

## API Reference

A date range picker allows the user to select a range of dates.

**Returns:** `Element` The date range picker element.

<ParamTable param={{"module_name": "deephaven.ui.", "name": "date_range_picker", "parameters": [{"name": "placeholder_value", "type": "DType | None | str | date | datetime | datetime64 | Timestamp | int", "description": "A placeholder date that influences the format of the placeholder shown when no value is selected. Defaults to today at midnight in the user's timezone."}, {"name": "value", "type": "DateRange | None", "description": "The current value (controlled).", "default": "None"}, {"name": "default_value", "type": "DateRange | None", "description": "The default value (uncontrolled).", "default": "None"}, {"name": "min_value", "type": "DType | None | str | date | datetime | datetime64 | Timestamp | int", "description": "The minimum allowed date that a user may select.", "default": "None"}, {"name": "max_value", "type": "DType | None | str | date | datetime | datetime64 | Timestamp | int", "description": "The maximum allowed date that a user may select.", "default": "None"}, {"name": "granularity", "type": "Literal['DAY', 'HOUR', 'MINUTE', 'SECOND'] | None", "description": "Determines the smallest unit that is displayed in the date picker. By default, this is \"DAY\" for LocalDate, and \"SECOND\" otherwise.", "default": "None"}, {"name": "page_behavior", "type": "Literal['single', 'visible'] | None", "description": "Controls the behavior of paging. Pagination either works by advancing the visible page by visibleDuration (default) or one unit of visibleDuration.", "default": "None"}, {"name": "hour_cycle", "type": "Literal[12, 24] | None", "description": "Whether to display the time in 12 or 24 hour format. By default, this is determined by the user's locale.", "default": "None"}, {"name": "hide_time_zone", "type": "bool", "description": "Whether to hide the time zone abbreviation.", "default": "False"}, {"name": "should_force_leading_zeros", "type": "bool | None", "description": "Whether to always show leading zeros in the month, day, and hour fields. By default, this is determined by the user's locale.", "default": "None"}, {"name": "is_disabled", "type": "bool | None", "description": "Whether the input is disabled.", "default": "None"}, {"name": "is_read_only", "type": "bool | None", "description": "Whether the input can be selected but not changed by the user.", "default": "None"}, {"name": "is_required", "type": "bool | None", "description": "Whether user input is required on the input before form submission.", "default": "None"}, {"name": "validation_behavior", "type": "Literal['aria', 'native'] | None", "description": "Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA.", "default": "None"}, {"name": "auto_focus", "type": "bool | None", "description": "Whether the element should receive focus on render.", "default": "None"}, {"name": "label", "type": "Element | None", "description": "The content to display as the label.", "default": "None"}, {"name": "description", "type": "Element | None", "description": "A description for the field. Provides a hint such as specific requirements for what to choose.", "default": "None"}, {"name": "error_message", "type": "Element | None", "description": "An error message for the field.", "default": "None"}, {"name": "is_open", "type": "bool | None", "description": "Whether the overlay is open by default (controlled).", "default": "None"}, {"name": "default_open", "type": "bool | None", "description": "Whether the overlay is open by default (uncontrolled).", "default": "None"}, {"name": "allows_non_contiguous_ranges", "type": "bool | None", "description": "When combined with unavailable_values, determines whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected.", "default": "None"}, {"name": "start_name", "type": "str | None", "description": "The name of the start date input element, used when submitting an HTML form.", "default": "None"}, {"name": "end_name", "type": "str | None", "description": "The name of the end date input element, used when submitting an HTML form.", "default": "None"}, {"name": "max_visible_months", "type": "int | None", "description": "The maximum number of months to display at once in the calendar popover, if screen space permits.", "default": "None"}, {"name": "should_flip", "type": "bool | None", "description": "Whether the calendar popover should automatically flip direction when space is limited.", "default": "None"}, {"name": "is_quiet", "type": "bool | None", "description": "Whether the date picker should be displayed with a quiet style.", "default": "None"}, {"name": "show_format_help_text", "type": "bool | None", "description": "Whether to show the localized date format as help text below the field.", "default": "None"}, {"name": "label_position", "type": "Literal['top', 'side'] | None", "description": "The label's overall position relative to the element it is labeling.", "default": "None"}, {"name": "label_align", "type": "Literal['start', 'end'] | None", "description": "The label's horizontal alignment relative to the element it is labeling.", "default": "None"}, {"name": "necessity_indicator", "type": "Literal['icon', 'label'] | None", "description": "Whether the required state should be shown as an icon or text.", "default": "None"}, {"name": "contextual_help", "type": "Element | None", "description": "A ContextualHelp element to place next to the label.", "default": "None"}, {"name": "validation_state", "type": "Literal['valid', 'invalid'] | None", "description": "Whether the input should display its \"valid\" or \"invalid\" visual styling.", "default": "None"}, {"name": "on_focus", "type": "Callable[[FocusEvent], None] | None", "description": "Function called when the button receives focus.", "default": "None"}, {"name": "on_blur", "type": "Callable[[FocusEvent], None] | None", "description": "Function called when the button loses focus.", "default": "None"}, {"name": "on_focus_change", "type": "Callable[[bool], None] | None", "description": "Function called when the focus state changes.", "default": "None"}, {"name": "on_key_down", "type": "Callable[[KeyboardEvent], None] | None", "description": "Function called when a key is pressed.", "default": "None"}, {"name": "on_key_up", "type": "Callable[[KeyboardEvent], None] | None", "description": "Function called when a key is released.", "default": "None"}, {"name": "on_open_change", "type": "Callable[[bool], None] | None", "description": "Handler that is called when the overlay's open state changes.", "default": "None"}, {"name": "on_change", "type": "Callable[[DateRange], None] | None", "description": "Handler that is called when the value changes. The exact Date type will be the same as the type passed to value, default_value or placeholder_value, in that order of precedence.", "default": "None"}, {"name": "flex", "type": "str | float | bool | None", "description": "When used in a flex layout, specifies how the element will grow or shrink to fit the space available.", "default": "None"}, {"name": "flex_grow", "type": "float | None", "description": "When used in a flex layout, specifies how much the element will grow to fit the space available.", "default": "None"}, {"name": "flex_shrink", "type": "float | None", "description": "When used in a flex layout, specifies how much the element will shrink to fit the space available.", "default": "None"}, {"name": "flex_basis", "type": "str | float | None", "description": "When used in a flex layout, specifies the initial size of the element.", "default": "None"}, {"name": "align_self", "type": "Literal['auto', 'normal', 'start', 'end', 'center', 'flex-start', 'flex-end', 'self-start', 'self-end', 'stretch'] | None", "description": "Overrides the align_items property of a flex or grid container.", "default": "None"}, {"name": "justify_self", "type": "Literal['auto', 'normal', 'start', 'end', 'flex-start', 'flex-end', 'self-start', 'self-end', 'center', 'left', 'right', 'stretch'] | None", "description": "Specifies how the element is justified inside a flex or grid container.", "default": "None"}, {"name": "order", "type": "int | None", "description": "The layout order for the element within a flex or grid container.", "default": "None"}, {"name": "grid_area", "type": "str | None", "description": "The name of the grid area to place the element in.", "default": "None"}, {"name": "grid_row", "type": "str | None", "description": "The name of the grid row to place the element in.", "default": "None"}, {"name": "grid_row_start", "type": "str | None", "description": "The name of the grid row to start the element in.", "default": "None"}, {"name": "grid_row_end", "type": "str | None", "description": "The name of the grid row to end the element in.", "default": "None"}, {"name": "grid_column", "type": "str | None", "description": "The name of the grid column to place the element in.", "default": "None"}, {"name": "grid_column_start", "type": "str | None", "description": "The name of the grid column to start the element in.", "default": "None"}, {"name": "grid_column_end", "type": "str | None", "description": "The name of the grid column to end the element in.", "default": "None"}, {"name": "margin", "type": "str | float | None", "description": "The margin to apply around the element.", "default": "None"}, {"name": "margin_top", "type": "str | float | None", "description": "The margin to apply above the element.", "default": "None"}, {"name": "margin_bottom", "type": "str | float | None", "description": "The margin to apply below the element.", "default": "None"}, {"name": "margin_start", "type": "str | float | None", "description": "The margin to apply before the element.", "default": "None"}, {"name": "margin_end", "type": "str | float | None", "description": "The margin to apply after the element.", "default": "None"}, {"name": "margin_x", "type": "str | float | None", "description": "The margin to apply to the left and right of the element.", "default": "None"}, {"name": "margin_y", "type": "str | float | None", "description": "The margin to apply to the top and bottom of the element.", "default": "None"}, {"name": "width", "type": "str | float | None", "description": "The width of the element.", "default": "None"}, {"name": "height", "type": "str | float | None", "description": "The height of the element.", "default": "None"}, {"name": "min_width", "type": "str | float | None", "description": "The minimum width of the element.", "default": "None"}, {"name": "min_height", "type": "str | float | None", "description": "The minimum height of the element.", "default": "None"}, {"name": "max_width", "type": "str | float | None", "description": "The maximum width of the element.", "default": "None"}, {"name": "max_height", "type": "str | float | None", "description": "The maximum height of the element.", "default": "None"}, {"name": "position", "type": "Literal['static', 'relative', 'absolute', 'fixed', 'sticky'] | None", "description": "Specifies how the element is positioned.", "default": "None"}, {"name": "top", "type": "str | float | None", "description": "The distance from the top of the containing element.", "default": "None"}, {"name": "bottom", "type": "str | float | None", "description": "The distance from the bottom of the containing element.", "default": "None"}, {"name": "start", "type": "str | float | None", "description": "The distance from the start of the containing element.", "default": "None"}, {"name": "end", "type": "str | float | None", "description": "The distance from the end of the containing element.", "default": "None"}, {"name": "left", "type": "str | float | None", "description": "The distance from the left of the containing element.", "default": "None"}, {"name": "right", "type": "str | float | None", "description": "The distance from the right of the containing element.", "default": "None"}, {"name": "z_index", "type": "int | None", "description": "The stack order of the element.", "default": "None"}, {"name": "is_hidden", "type": "bool | None", "description": "Whether the element is hidden.", "default": "None"}, {"name": "id", "type": "str | None", "description": "A unique identifier for the element.", "default": "None"}, {"name": "aria_label", "type": "str | None", "description": "The label for the element.", "default": "None"}, {"name": "aria_labelledby", "type": "str | None", "description": "The id of the element that labels the element.", "default": "None"}, {"name": "aria_describedby", "type": "str | None", "description": "The id of the element that describes the element.", "default": "None"}, {"name": "aria_pressed", "type": "Literal['true', 'false'] | bool | Literal['mixed'] | None", "description": "Whether the element is pressed.", "default": "None"}, {"name": "aria_details", "type": "str | None", "description": "The details for the element.", "default": "None"}, {"name": "UNSAFE_class_name", "type": "str | None", "description": "A CSS class to apply to the element.", "default": "None"}, {"name": "UNSAFE_style", "type": "Dict[str, Any] | None", "description": "A CSS style to apply to the element.", "default": "None"}, {"name": "key", "type": "str | None", "description": "A unique identifier used by React to render elements in a list.", "default": "None"}]}} />
