code: def _date_input(
      self,
      label: str,
      value: DateValue | Literal["today"] = "today",
      min_value: SingleDateValue = None,
      max_value: SingleDateValue = None,
      key: Key | None = None,
      help: str | None = None,
      on_change: WidgetCallback | None = None,
      args: WidgetArgs | None = None,
      kwargs: WidgetKwargs | None = None,
      *,  # keyword-only arguments:
      format: str = "YYYY/MM/DD",
      disabled: bool = False,
      label_visibility: LabelVisibility = "visible",
      ctx: ScriptRunContext | None = None,
  ) -> DateWidgetReturn:
      key = to_key(key)
      check_callback_rules(self.dg, on_change)
      check_session_state_rules(
          default_value=value if value != "today" else None, key=key
      )
  
      maybe_raise_label_warnings(label, label_visibility)
  
      def parse_date_deterministic(
          v: SingleDateValue | Literal["today"],
      ) -> str | None:
          if isinstance(v, datetime):
              return date.strftime(v.date(), "%Y/%m/%d")
          elif isinstance(v, date):
              return date.strftime(v, "%Y/%m/%d")
          return None
  
      parsed_min_date = parse_date_deterministic(min_value)
      parsed_max_date = parse_date_deterministic(max_value)
  
      parsed: str | None | List[str | None]
      if value == "today" or value is None:
          parsed = None
      elif isinstance(value, (datetime, date)):
          parsed = parse_date_deterministic(value)
      else:
          parsed = [
              parse_date_deterministic(cast(SingleDateValue, v))
              for v in value
          ]
  
      # TODO this is missing the error path, integrate with the dateinputvalues parsing
  
      id = compute_widget_id(
          "date_input",
          user_key=key,
          label=label,
          value=parsed,
          min_value=parsed_min_date,
          max_value=parsed_max_date,
          key=key,
          help=help,
          format=format,
          form_id=current_form_id(self.dg),
          page=ctx.page_script_hash if ctx else None,
      )
      if not bool(ALLOWED_DATE_FORMATS.match(format)):
          raise StreamlitAPIException(
              f"The provided format (`{format}`) is not valid. DateInput format "
              "should be one of `YYYY/MM/DD`, `DD/MM/YYYY`, or `MM/DD/YYYY` "
              "and can also use a period (.) or hyphen (-) as separators."
          )
  
      parsed_values = _DateInputValues.from_raw_values(
          value=value,
          min_value=min_value,
          max_value=max_value,
      )
      del value, min_value, max_value
  
      date_input_proto = DateInputProto()
      date_input_proto.id = id
      date_input_proto.is_range = parsed_values.is_range
      date_input_proto.disabled = disabled
      date_input_proto.label_visibility.value = (
          get_label_visibility_proto_value(label_visibility)
      )
      date_input_proto.format = format
      date_input_proto.label = label
      if parsed_values.value is None:
          # An empty array represents the empty state. The reason for using an empty
          # array here is that we cannot optional keyword for repeated fields
          # in protobuf.
          date_input_proto.default[:] = []
      else:
          date_input_proto.default[:] = [
              date.strftime(v, "%Y/%m/%d") for v in parsed_values.value
          ]
      date_input_proto.min = date.strftime(
          parsed_values.min, "%Y/%m/%d"
      )
      date_input_proto.max = date.strftime(
          parsed_values.max, "%Y/%m/%d"
      )
      date_input_proto.form_id = current_form_id(self.dg)
  
      if help is not None:
          date_input_proto.help = dedent(help)
  
      serde = DateInputSerde(parsed_values)
  
      widget_state = register_widget(
          "date_input",
          date_input_proto,
          user_key=key,
          on_change_handler=on_change,
          args=args,
          kwargs=kwargs,
          deserializer=serde.deserialize,
          serializer=serde.serialize,
          ctx=ctx,
      )
  
      if widget_state.value_changed:
          date_input_proto.value[:] = serde.serialize(
              widget_state.value
          )
          date_input_proto.set_value = True
  
      self.dg._enqueue("date_input", date_input_proto)
      return widget_state.value
  
imports: from __future__ import annotations
  import re
  from dataclasses import dataclass
  from datetime import date, datetime, time, timedelta
  from textwrap import dedent
  from typing import TYPE_CHECKING, Any, List, Literal, Sequence, Tuple, Union, cast, overload
  from dateutil import relativedelta
  from typing_extensions import TypeAlias
  from streamlit.elements.form import current_form_id
  from streamlit.elements.utils import check_callback_rules, check_session_state_rules, get_label_visibility_proto_value
  from streamlit.errors import StreamlitAPIException
  from streamlit.proto.DateInput_pb2 import DateInput
  from streamlit.proto.TimeInput_pb2 import TimeInput
  from streamlit.runtime.metrics_util import gather_metrics
  from streamlit.runtime.scriptrunner import ScriptRunContext, get_script_run_ctx
  from streamlit.runtime.state import WidgetArgs, WidgetCallback, WidgetKwargs, register_widget
  from streamlit.runtime.state.common import compute_widget_id
  from streamlit.type_util import Key, LabelVisibility, maybe_raise_label_warnings, to_key
label_desc: Use image node_id 6 for calling the TimeWidgetsMixin obj's underlying member method code with example usage: obj._date_input(label, value, min_value, max_value, key, help, on_change, args, kwargs) and returns: widget_state, None, date, date