naturaltime function

String naturaltime(
  1. DateTime moment, {
  2. DateTime? now,
  3. String locale = 'en',
})

Human readable relative time, e.g. "3 minutes ago" or "in 2 hours". For differences over a day, combines units (e.g., "1 week, 2 days").

Implementation

String naturaltime(DateTime moment, {DateTime? now, String locale = 'en'}) {
  final base = now ?? DateTime.now();
  final delta = moment.difference(base);
  final abs = delta.abs();
  final isFr = locale.toLowerCase().startsWith('fr');

  String phrase(String amount) {
    if (abs.inSeconds == 0) {
      return isFr ? 'maintenant' : 'now';
    }
    if (delta.isNegative) {
      return isFr ? 'il y a $amount' : '$amount ago';
    } else {
      return isFr ? 'dans $amount' : 'in $amount';
    }
  }

  String enUnit(int n, String sing, String plur) => n == 1 ? '1 $sing' : '$n $plur';
  String frUnit(int n, String sing, String plur, {bool fem = false}) {
    if (n == 1) {
      return fem ? 'une $sing' : 'un $sing';
    }
    return '$n $plur';
  }

  // Combine for >= 1 day
  if (abs.inDays >= 1) {
    final days = abs.inDays;
    final weeks = days ~/ 7;
    final remDays = days % 7;
    if (weeks >= 1) {
      final part1 = isFr
          ? frUnit(weeks, 'semaine', 'semaines', fem: true)
          : enUnit(weeks, 'week', 'weeks');
      if (remDays > 0) {
        final part2 = isFr
            ? frUnit(remDays, 'jour', 'jours')
            : enUnit(remDays, 'day', 'days');
        return phrase('$part1, $part2');
      }
      return phrase(part1);
    }
    // < 7 days: days + hours
    final hours = abs.inHours % 24;
    final part1 = isFr ? frUnit(days, 'jour', 'jours') : enUnit(days, 'day', 'days');
    if (hours > 0) {
      final part2 = isFr ? frUnit(hours, 'heure', 'heures', fem: true) : enUnit(hours, 'hour', 'hours');
      return phrase('$part1, $part2');
    }
    return phrase(part1);
  }

  if (abs.inHours >= 1) {
    final n = abs.inHours;
    final text = isFr ? frUnit(n, 'heure', 'heures', fem: true) : enUnit(n, 'hour', 'hours');
    return phrase(text);
  }
  if (abs.inMinutes >= 1) {
    final n = abs.inMinutes;
    final text = isFr ? frUnit(n, 'minute', 'minutes', fem: true) : enUnit(n, 'minute', 'minutes');
    return phrase(text);
  }
  final n = abs.inSeconds;
  final text = isFr ? frUnit(n, 'seconde', 'secondes', fem: true) : enUnit(n, 'second', 'seconds');
  return phrase(text);
}