splitAt method

List<Period> splitAt(
  1. Set<DateTime> dates, {
  2. Duration periodBetween = Duration.zero,
})

Splits the period in multiple periods at the given dates. The periodBetween is the duration between each period. The dates not included in the period are ignored. The dates will be sorted before splitting. If dates contain the start or end of the period, they will be ignored since they are already included in the period.

The periodBetween must be greater than or equals to zero and less than the duration of the period. The sum of the period between dates must be less than the duration of the period.

If the periodBetween is zero, the periods will be continuous. If the periodBetween is greater than zero, the periods will be separated by the given duration.

If the dates are empty, the period is returned. If the dates are not empty, the period is split at the given dates.

Example:

final period = Period(
  start: DateTime(2020, 1, 1),
  end: DateTime(2020, 1, 31),
);

final periods = period.splitAt(
  {
    DateTime(2020, 1, 10),
    DateTime(2020, 1, 20),
  },
  periodBetween: const Duration(days: 1),
);

// periods = [
//   Period(
//     start: DateTime(2020, 1, 1),
//     end: DateTime(2020, 1, 10),
//   ),
//   Period(
//     start: DateTime(2020, 1, 11),
//     end: DateTime(2020, 1, 20),
//   ),
//   Period(
//     start: DateTime(2020, 1, 21),
//     end: DateTime(2020, 1, 31),
//   ),
// ]

Implementation

List<Period> splitAt(
  Set<DateTime> dates, {
  Duration periodBetween = Duration.zero,
}) {
  if ((periodBetween < Duration.zero) || (duration <= periodBetween)) {
    throw ArgumentError.value(
      periodBetween,
      'periodBetween',
      'The period between dates must be greater than or equals to zero and '
          'less than the duration of the period.',
    );
  }
  final periods = <Period>[];
  final sortedValidDates = [...dates.where(contains).whereNot(_startOrEnd)]
    ..sort();
  final resultDuration = periodBetween * sortedValidDates.length;
  if (resultDuration > duration) {
    throw ArgumentError.value(
      dates,
      'dates',
      'The sum of the period between dates is greater than the duration of '
          'the period.',
    );
  }
  if (sortedValidDates.isNotEmpty) {
    sortedValidDates.add(end);
    if (sortedValidDates
        .mapPairs((a, b) => b.difference(a))
        .any((difference) => difference < periodBetween)) {
      throw ArgumentError.value(
        periodBetween,
        'periodBetween',
        'The period between the provided dates must be greater than or '
            'equal to $periodBetween',
      );
    }
    periods.add(Period(start: start, end: sortedValidDates.first));
    sortedValidDates.removeAt(0);
  }
  for (final date in sortedValidDates) {
    periods.add(
      Period(start: periods.last.end.add(periodBetween), end: date),
    );
  }
  if (!periods.isNotEmpty) {
    periods.add(this);
  }
  return periods;
}