diff --git a/examples/reference/widgets/DatetimePicker.ipynb b/examples/reference/widgets/DatetimePicker.ipynb index 97081faa51..7dae3bd058 100644 --- a/examples/reference/widgets/DatetimePicker.ipynb +++ b/examples/reference/widgets/DatetimePicker.ipynb @@ -28,8 +28,8 @@ "##### Core\n", "\n", "* **``value``** (datetime): The selected value as a datetime type\n", - "* **``start``** (date or datetime): Inclusive lower bound of the allowed date selection. Note that while the parameter accepts datetimes the time portion of the range is ignored.\n", - "* **``end``** (date or datetime): Inclusive upper bound of the allowed date selection. Note that while the parameter accepts datetimes the time portion of the range is ignored.\n", + "* **``start``** (date or datetime): Inclusive lower bound of the allowed date selection.\n", + "* **``end``** (date or datetime): Inclusive upper bound of the allowed date selection.\n", "* **``disabled_dates``** (list): Dates to make unavailable for selection; others will be available\n", "* **``enabled_dates``** (list): Dates to make available for selection; others will be unavailable\n", "* **``enable_time``** (boolean): Enable editing of the time in the widget, default is True\n", diff --git a/examples/reference/widgets/DatetimeRangePicker.ipynb b/examples/reference/widgets/DatetimeRangePicker.ipynb index 9812fa0596..1616af1a28 100644 --- a/examples/reference/widgets/DatetimeRangePicker.ipynb +++ b/examples/reference/widgets/DatetimeRangePicker.ipynb @@ -28,8 +28,8 @@ "##### Core\n", "\n", "* **``value``** (tuple): Tuple of upper and lower bounds of the selected range expressed as datetime types\n", - "* **``start``** (date or datetime): Inclusive lower bound of the allowed date selection. Note that while the parameter accepts datetimes the time portion of the range is ignored.\n", - "* **``end``** (date or datetime): Inclusive upper bound of the allowed date selection. Note that while the parameter accepts datetimes the time portion of the range is ignored.\n", + "* **``start``** (date or datetime): Inclusive lower bound of the allowed date selection. \n", + "* **``end``** (date or datetime): Inclusive upper bound of the allowed date selection. \n", "* **``disabled_dates``** (list): Dates to make unavailable for selection; others will be available\n", "* **``enabled_dates``** (list): Dates to make available for selection; others will be unavailable\n", "* **``enable_time:``** (boolean): Enable editing of the time in the widget, default is True\n", diff --git a/panel/models/datetime_picker.ts b/panel/models/datetime_picker.ts index 954cf67b6f..944422dd43 100644 --- a/panel/models/datetime_picker.ts +++ b/panel/models/datetime_picker.ts @@ -69,8 +69,8 @@ export class DatetimePickerView extends InputWidgetView { this.group_el.appendChild(this.input_el) this._picker = flatpickr(this.input_el, { defaultDate: this.model.value, - minDate: this.model.min_date ?? undefined, - maxDate: this.model.max_date ?? undefined, + minDate: this.model.min_date ? new Date(this.model.min_date) : undefined, + maxDate: this.model.max_date ? new Date(this.model.max_date) : undefined, inline: this.model.inline, position: this.model.position, disable: _convert_date_list(this.model.disabled_dates), @@ -82,6 +82,8 @@ export class DatetimePickerView extends InputWidgetView { mode: this.model.mode, onClose: (selected_dates, date_string, instance) => this._on_close(selected_dates, date_string, instance), }) + this._picker.maxDateHasTime = true + this._picker.minDateHasTime = true } protected _on_close(_selected_dates: Date[], date_string: string, _instance: flatpickr.Instance): void { diff --git a/panel/tests/widgets/test_input.py b/panel/tests/widgets/test_input.py index 0a1d83298f..01faa4e587 100644 --- a/panel/tests/widgets/test_input.py +++ b/panel/tests/widgets/test_input.py @@ -61,7 +61,7 @@ def test_date_picker(document, comm): def test_datetime_picker(document, comm): datetime_picker = DatetimePicker( name='DatetimePicker', value=datetime(2018, 9, 2, 1, 5), - start=date(2018, 9, 1), end=date(2018, 9, 10) + start=datetime(2018, 9, 1), end=datetime(2018, 9, 10) ) widget = datetime_picker.get_root(document, comm=comm) @@ -69,8 +69,8 @@ def test_datetime_picker(document, comm): assert isinstance(widget, datetime_picker._widget_type) assert widget.title == 'DatetimePicker' assert widget.value == '2018-09-02 01:05:00' - assert widget.min_date == '2018-09-01' - assert widget.max_date == '2018-09-10' + assert widget.min_date == '2018-09-01T00:00:00' + assert widget.max_date == '2018-09-10T00:00:00' datetime_picker._process_events({'value': '2018-09-03 03:00:01'}) assert datetime_picker.value == datetime(2018, 9, 3, 3, 0, 1) @@ -85,15 +85,20 @@ def test_datetime_picker(document, comm): assert datetime_picker._deserialize_value(value) == '2018-09-04 12:01:00' assert datetime_picker._serialize_value(datetime_picker._deserialize_value(value)) == value + # Check start value + with pytest.raises(ValueError): + datetime_picker._process_events({'value': '2018-08-31 23:59:59'}) + # Check end value - datetime_picker._process_events({'value': '2018-09-10 03:00:01'}) - assert datetime_picker.value == datetime(2018, 9, 10, 0, 0, 0) + with pytest.raises(ValueError): + datetime_picker._process_events({'value': '2018-09-10 00:00:01'}) + def test_datetime_range_picker(document, comm): datetime_range_picker = DatetimeRangePicker( name='DatetimeRangePicker', value=(datetime(2018, 9, 2, 1, 5), datetime(2018, 9, 2, 1, 6)), - start=date(2018, 9, 1), end=date(2018, 9, 10) + start=datetime(2018, 9, 1), end=datetime(2018, 9, 10) ) widget = datetime_range_picker.get_root(document, comm=comm) @@ -101,8 +106,8 @@ def test_datetime_range_picker(document, comm): assert isinstance(widget, datetime_range_picker._widget_type) assert widget.title == 'DatetimeRangePicker' assert widget.value == '2018-09-02 01:05:00 to 2018-09-02 01:06:00' - assert widget.min_date == '2018-09-01' - assert widget.max_date == '2018-09-10' + assert widget.min_date == '2018-09-01T00:00:00' + assert widget.max_date == '2018-09-10T00:00:00' datetime_range_picker._process_events({'value': '2018-09-03 03:00:01 to 2018-09-04 03:00:01'}) assert datetime_range_picker.value == (datetime(2018, 9, 3, 3, 0, 1), datetime(2018, 9, 4, 3, 0, 1)) @@ -116,9 +121,14 @@ def test_datetime_range_picker(document, comm): assert datetime_range_picker._deserialize_value(value) == '2018-09-04 12:01:00 to 2018-09-04 12:01:10' assert datetime_range_picker._serialize_value(datetime_range_picker._deserialize_value(value)) == value + # Check start value + with pytest.raises(ValueError): + datetime_range_picker._process_events({'value': '2018-08-31 23:59:59'}) + # Check end value - datetime_range_picker._process_events({'value': '2018-09-09 03:00:01 to 2018-09-10 03:00:01'}) - assert datetime_range_picker.value == (datetime(2018, 9, 9, 3, 0, 1), datetime(2018, 9, 10, 0, 0, 0)) + with pytest.raises(ValueError): + datetime_range_picker._process_events({'value': '2018-09-10 00:00:01'}) + def test_file_input(document, comm): diff --git a/panel/widgets/input.py b/panel/widgets/input.py index 128fccc28c..4b0961248e 100644 --- a/panel/widgets/input.py +++ b/panel/widgets/input.py @@ -327,17 +327,9 @@ def __init__(self, **params): super().__init__(**params) self._update_value_bounds() - @staticmethod - def _date_to_datetime(x): - if isinstance(x, date): - return datetime(x.year, x.month, x.day) - @param.depends('start', 'end', watch=True) def _update_value_bounds(self): - self.param.value.bounds = ( - self._date_to_datetime(self.start), - self._date_to_datetime(self.end), - ) + self.param.value.bounds = (self.start, self.end) self.param.value._validate(self.value) def _process_property_change(self, msg): @@ -378,11 +370,6 @@ def _serialize_value(self, value): if isinstance(value, str) and value: value = datetime.strptime(value, r'%Y-%m-%d %H:%M:%S') - # Hour, minute and seconds can be increased after end is reached. - # This forces the hours, minute and second to be 0. - end = self._date_to_datetime(self.end) - if end is not None and value > end: - value = end return value @@ -421,13 +408,6 @@ def _serialize_value(self, value): for value in value.split(' to ') ] - # Hour, minute and seconds can be increased after end is reached. - # This forces the hours, minute and second to be 0. - end = self._date_to_datetime(self.end) - if end is not None and value[0] > end: - value[0] = end - if end is not None and value[1] > end: - value[1] = end value = tuple(value)