def _apply_data_transformers(
    series: Union[TimeSeries, list[TimeSeries]],
    past_covariates: Optional[Union[TimeSeries, list[TimeSeries]]],
    future_covariates: Optional[Union[TimeSeries, list[TimeSeries]]],
    data_transformers: dict[str, Pipeline],
    max_future_cov_lag: int,
    fit_transformers: bool,
) -> tuple[
    Union[TimeSeries, list[TimeSeries]],
    Union[TimeSeries, list[TimeSeries]],
    Union[TimeSeries, list[TimeSeries]],
]:
    """Transform each series using the corresponding Pipeline.

    If the Pipeline is fittable and `fit_transformers=True`, the series are sliced to correspond
    to the information available at model training time
    """
    # `global_fit`` is not supported, requires too complex time indexes manipulation across series (slice and align)
    if fit_transformers and any(
        not (isinstance(ts, TimeSeries) or ts is None)
        for ts in [series, past_covariates, future_covariates]
    ):
        raise_log(
            ValueError(
                "Fitting the data transformers on multiple series is not supported, either provide trained "
                "`data_transformers` or a single series (including for the covariates).",
                logger,
            )
        )
    transformed_ts = []
    for ts_type, ts in zip(
        ["series", "past_covariates", "future_covariates"],
        [series, past_covariates, future_covariates],
    ):
        if ts is None or data_transformers.get(ts_type) is None:
            transformed_ts.append(ts)
        else:
            if fit_transformers and data_transformers[ts_type].fittable:
                # must slice the ts to distinguish accessible information from future information
                if ts_type == "past_covariates":
                    # known information is aligned with the target series
                    tmp_ts = ts.drop_after(series.end_time())
                elif ts_type == "future_covariates":
                    # known information goes up to the first forecasts iteration (in case of autoregression)
                    tmp_ts = ts.drop_after(
                        series.end_time() + max(0, max_future_cov_lag + 1) * series.freq
                    )
                else:
                    # nothing to do, the target series is already sliced appropriately
                    tmp_ts = ts
                data_transformers[ts_type].fit(tmp_ts)
            # transforming the series
            transformed_ts.append(data_transformers[ts_type].transform(ts))
    return tuple(transformed_ts)
Transform each series using the corresponding Pipeline.

    If the Pipeline is fittable and `fit_transformers=True`, the series are sliced to correspond
    to the information available at model training time
    
darts/utils/historical_forecasts/utils.py