duration function

String duration(
  1. num milliseconds, {
  2. bool useShortString = false,
  3. bool useSpace = true,
  4. bool withZeroValue = false,
  5. String separator = ' ',
  6. bool withMilliSeconds = false,
  7. int? maxUnitCount,
  8. String? unit,
})

Displays the given millisecond value in human-readable time. For example, the value of 604800000 (7 days) is displayed as 7 Days. A month is treated as 30 days and a year as 365 days.

  • useShortString: Days -> D, Hours -> H, Minutes -> M, Seconds -> S, Months -> Mo, Years -> Y, Milliseconds -> ms.
  • useSpace: Insert a space between value and unit (e.g. 1Days -> 1 Days).
  • withZeroValue: Include units with a value of 0 below the largest unit.
  • separator: Joins each unit (e.g. - -> 1 Hour-10 Minutes).
  • withMilliSeconds: Include the millisecond unit (default false).
  • maxUnitCount: Maximum number of units to display, counted from the largest.
  • unit: Show the whole duration with a single unit (e.g. Hour -> 48 Hours, 0.5 Hours).

Implementation

String duration(
  num milliseconds, {
  bool useShortString = false,
  bool useSpace = true,
  bool withZeroValue = false,
  String separator = ' ',
  bool withMilliSeconds = false,
  int? maxUnitCount,
  String? unit,
}) {
  // Single-unit mode: express the whole duration with one unit (fractions allowed).
  if (unit != null) {
    String name = unit.toLowerCase();
    if (name.endsWith('s')) {
      name = name.substring(0, name.length - 1);
    }
    Map<String, dynamic>? target;
    for (final u in _durationUnits) {
      if ((u['name'] as String).toLowerCase() == name) {
        target = u;
        break;
      }
    }
    if (target != null) {
      final num value = double.parse(
          (milliseconds / (target['ms'] as int)).toStringAsFixed(6));
      return _durationLabel(value, target, useShortString, useSpace);
    }
  }

  final List<Map<String, dynamic>> activeUnits = withMilliSeconds
      ? _durationUnits
      : _durationUnits.where((u) => u['name'] != 'Millisecond').toList();

  final List<Map<String, dynamic>> values = [];
  num remaining = milliseconds;
  for (final u in activeUnits) {
    final int value = (remaining / (u['ms'] as int)).floor();
    remaining -= value * (u['ms'] as int);
    values.add({'value': value, 'unit': u});
  }

  // Skip leading units that are zero; keep interior/trailing zeros only when requested.
  int firstNonZero = -1;
  for (int i = 0; i < values.length; i++) {
    if (values[i]['value'] != 0) {
      firstNonZero = i;
      break;
    }
  }
  if (firstNonZero == -1) {
    return '';
  }

  List<Map<String, dynamic>> selected = values.sublist(firstNonZero);
  if (!withZeroValue) {
    selected = selected.where((v) => v['value'] != 0).toList();
  }

  List<String> result = selected
      .map((v) => _durationLabel(v['value'] as num,
          v['unit'] as Map<String, dynamic>, useShortString, useSpace))
      .toList();

  if (maxUnitCount != null && maxUnitCount >= 0) {
    result = result.take(maxUnitCount).toList();
  }

  return result.join(separator);
}