parseDuration method

Duration parseDuration()

Parses this string into a Duration.

Supported formats:

  • Clock format: "HH:mm:ss" or "mm:ss"
  • Token format: "1h 20m", "2d 3h 4m 5s"

Throws FormatException for invalid input.

Implementation

Duration parseDuration() {
  final original = this;
  var input = trim();
  if (input.isEmpty) {
    throw FormatException('Invalid duration', original);
  }

  var isNegative = false;
  if (input.startsWith('-')) {
    isNegative = true;
    input = input.substring(1).trimLeft();
  }

  if (input.isEmpty) {
    throw FormatException('Invalid duration', original);
  }

  FormatException invalid([String? reason]) =>
      FormatException(reason ?? 'Invalid duration', original);

  Duration parseClock(String value) {
    final parts = value.split(':');
    if (parts.length < 2 || parts.length > 3) {
      throw invalid('Invalid clock duration');
    }

    int hours = 0;
    int minutes = 0;
    int seconds = 0;

    try {
      if (parts.length == 3) {
        hours = int.parse(parts[0]);
        minutes = int.parse(parts[1]);
        seconds = int.parse(parts[2]);
        if (minutes >= 60 || seconds >= 60) {
          throw invalid('Invalid clock duration');
        }
      } else {
        minutes = int.parse(parts[0]);
        seconds = int.parse(parts[1]);
        if (seconds >= 60) {
          throw invalid('Invalid clock duration');
        }
      }
    } on FormatException {
      throw invalid('Invalid clock duration');
    }

    if (hours < 0 || minutes < 0 || seconds < 0) {
      throw invalid('Invalid clock duration');
    }

    return Duration(hours: hours, minutes: minutes, seconds: seconds);
  }

  Duration parseTokens(String value) {
    final regex = RegExp(r'(\d+)\s*([dhms])', caseSensitive: false);
    final matches = regex.allMatches(value);
    if (matches.isEmpty) {
      throw invalid();
    }

    final leftover = value.replaceAll(regex, '').trim();
    if (leftover.isNotEmpty) {
      throw invalid();
    }

    var days = 0;
    var hours = 0;
    var minutes = 0;
    var seconds = 0;

    for (final match in matches) {
      final amount = int.parse(match.group(1)!);
      final unit = match.group(2)!.toLowerCase();
      switch (unit) {
        case 'd':
          days += amount;
          break;
        case 'h':
          hours += amount;
          break;
        case 'm':
          minutes += amount;
          break;
        case 's':
          seconds += amount;
          break;
      }
    }

    return Duration(
      days: days,
      hours: hours,
      minutes: minutes,
      seconds: seconds,
    );
  }

  final duration = input.contains(':')
      ? parseClock(input)
      : parseTokens(input.replaceAll(RegExp(r'\s+'), ' '));

  return isNegative
      ? Duration(microseconds: -duration.inMicroseconds)
      : duration;
}