def decode_cf_variable(
    name: Hashable,
    var: Variable,
    concat_characters: bool = True,
    mask_and_scale: bool = True,
    decode_times: bool | CFDatetimeCoder = True,
    decode_endianness: bool = True,
    stack_char_dim: bool = True,
    use_cftime: bool | None = None,
    decode_timedelta: bool | CFTimedeltaCoder | None = None,
) -> Variable:
    """
    Decodes a variable which may hold CF encoded information.

    This includes variables that have been masked and scaled, which
    hold CF style time variables (this is almost always the case if
    the dataset has been serialized) and which have strings encoded
    as character arrays.

    Parameters
    ----------
    name : str
        Name of the variable. Used for better error messages.
    var : Variable
        A variable holding potentially CF encoded information.
    concat_characters : bool
        Should character arrays be concatenated to strings, for
        example: ["h", "e", "l", "l", "o"] -> "hello"
    mask_and_scale : bool
        Lazily scale (using scale_factor and add_offset) and mask
        (using _FillValue). If the _Unsigned attribute is present
        treat integer arrays as unsigned.
    decode_times : bool or CFDatetimeCoder
        Decode cf times ("hours since 2000-01-01") to np.datetime64.
    decode_endianness : bool
        Decode arrays from non-native to native endianness.
    stack_char_dim : bool
        Whether to stack characters into bytes along the last dimension of this
        array. Passed as an argument because we need to look at the full
        dataset to figure out if this is appropriate.
    use_cftime : bool, optional
        Only relevant if encoded dates come from a standard calendar
        (e.g. "gregorian", "proleptic_gregorian", "standard", or not
        specified).  If None (default), attempt to decode times to
        ``np.datetime64[ns]`` objects; if this is not possible, decode times to
        ``cftime.datetime`` objects. If True, always decode times to
        ``cftime.datetime`` objects, regardless of whether or not they can be
        represented using ``np.datetime64[ns]`` objects.  If False, always
        decode times to ``np.datetime64[ns]`` objects; if this is not possible
        raise an error.

        .. deprecated:: 2025.01.1
           Please pass a :py:class:`coders.CFDatetimeCoder` instance initialized with ``use_cftime`` to the ``decode_times`` kwarg instead.
    decode_timedelta : None, bool, or CFTimedeltaCoder
        Decode cf timedeltas ("hours") to np.timedelta64.

    Returns
    -------
    out : Variable
        A variable holding the decoded equivalent of var.
    """
    # Ensure datetime-like Variables are passed through unmodified (GH 6453)
    if _contains_datetime_like_objects(var):
        return var

    original_dtype = var.dtype

    decode_timedelta_was_none = decode_timedelta is None
    if decode_timedelta is None:
        if isinstance(decode_times, CFDatetimeCoder):
            decode_timedelta = CFTimedeltaCoder(time_unit=decode_times.time_unit)
        else:
            decode_timedelta = bool(decode_times)

    if concat_characters:
        if stack_char_dim:
            var = strings.CharacterArrayCoder().decode(var, name=name)
        var = strings.EncodedStringCoder().decode(var)

    if original_dtype.kind == "O":
        var = variables.ObjectVLenStringCoder().decode(var)
        original_dtype = var.dtype

    if original_dtype.kind == "T":
        var = variables.Numpy2StringDTypeCoder().decode(var)

    if mask_and_scale:
        for coder in [
            variables.CFMaskCoder(
                decode_times=decode_times, decode_timedelta=decode_timedelta
            ),
            variables.CFScaleOffsetCoder(
                decode_times=decode_times, decode_timedelta=decode_timedelta
            ),
        ]:
            var = coder.decode(var, name=name)

    if decode_timedelta:
        if isinstance(decode_timedelta, bool):
            decode_timedelta = CFTimedeltaCoder(
                decode_via_units=decode_timedelta, decode_via_dtype=decode_timedelta
            )
        decode_timedelta._emit_decode_timedelta_future_warning = (
            decode_timedelta_was_none
        )
        var = decode_timedelta.decode(var, name=name)
    if decode_times:
        # remove checks after end of deprecation cycle
        if not isinstance(decode_times, CFDatetimeCoder):
            if use_cftime is not None:
                emit_user_level_warning(
                    "Usage of 'use_cftime' as a kwarg is deprecated. "
                    "Please pass a 'CFDatetimeCoder' instance initialized "
                    "with 'use_cftime' to the 'decode_times' kwarg instead.\n"
                    "Example usage:\n"
                    "    time_coder = xr.coders.CFDatetimeCoder(use_cftime=True)\n"
                    "    ds = xr.open_dataset(decode_times=time_coder)\n",
                    DeprecationWarning,
                )
            decode_times = CFDatetimeCoder(use_cftime=use_cftime)
        elif use_cftime is not None:
            raise TypeError(
                "Usage of 'use_cftime' as a kwarg is not allowed "
                "if a 'CFDatetimeCoder' instance is passed to "
                "'decode_times'. Please set 'use_cftime' "
                "when initializing 'CFDatetimeCoder' instead.\n"
                "Example usage:\n"
                "    time_coder = xr.coders.CFDatetimeCoder(use_cftime=True)\n"
                "    ds = xr.open_dataset(decode_times=time_coder)\n",
            )
        var = decode_times.decode(var, name=name)

    if decode_endianness and not var.dtype.isnative:
        var = variables.EndianCoder().decode(var)
        original_dtype = var.dtype

    var = variables.BooleanCoder().decode(var)

    dimensions, data, attributes, encoding = variables.unpack_for_decoding(var)

    encoding.setdefault("dtype", original_dtype)

    if not is_duck_dask_array(data):
        data = indexing.LazilyIndexedArray(data)

    return Variable(dimensions, data, attributes, encoding=encoding, fastpath=True)

    Decodes a variable which may hold CF encoded information.

    This includes variables that have been masked and scaled, which
    hold CF style time variables (this is almost always the case if
    the dataset has been serialized) and which have strings encoded
    as character arrays.

    Parameters
    ----------
    name : str
        Name of the variable. Used for better error messages.
    var : Variable
        A variable holding potentially CF encoded information.
    concat_characters : bool
        Should character arrays be concatenated to strings, for
        example: ["h", "e", "l", "l", "o"] -> "hello"
    mask_and_scale : bool
        Lazily scale (using scale_factor and add_offset) and mask
        (using _FillValue). If the _Unsigned attribute is present
        treat integer arrays as unsigned.
    decode_times : bool or CFDatetimeCoder
        Decode cf times ("hours since 2000-01-01") to np.datetime64.
    decode_endianness : bool
        Decode arrays from non-native to native endianness.
    stack_char_dim : bool
        Whether to stack characters into bytes along the last dimension of this
        array. Passed as an argument because we need to look at the full
        dataset to figure out if this is appropriate.
    use_cftime : bool, optional
        Only relevant if encoded dates come from a standard calendar
        (e.g. "gregorian", "proleptic_gregorian", "standard", or not
        specified).  If None (default), attempt to decode times to
        ``np.datetime64[ns]`` objects; if this is not possible, decode times to
        ``cftime.datetime`` objects. If True, always decode times to
        ``cftime.datetime`` objects, regardless of whether or not they can be
        represented using ``np.datetime64[ns]`` objects.  If False, always
        decode times to ``np.datetime64[ns]`` objects; if this is not possible
        raise an error.

        .. deprecated:: 2025.01.1
           Please pass a :py:class:`coders.CFDatetimeCoder` instance initialized with ``use_cftime`` to the ``decode_times`` kwarg instead.
    decode_timedelta : None, bool, or CFTimedeltaCoder
        Decode cf timedeltas ("hours") to np.timedelta64.

    Returns
    -------
    out : Variable
        A variable holding the decoded equivalent of var.
    
xarray/conventions.py