timeAgo static method

String timeAgo(
  1. DateTime date, {
  2. DateTime? now,
  3. bool assumeUtc = false,
  4. int justNowSeconds = 10,
  5. TimeAgoLabels? labels,
})

A relative "time ago" phrase covering seconds → years.

Robust by design: • Never emits a negative count — a future or clock-skewed date (e.g. a just-posted row whose server time is slightly ahead) clamps to the labels' "just now" instead of -3s ago. • Full range — seconds, minutes, hours, days, weeks, months, years. • Localisable — pass labels (TimeAgoLabels.en() / .enShort() / .ar(), or your own) so plural/dual rules stay out of the framework.

Parameters: • now — reference instant; inject in tests for determinism (defaults to DateTime.now()). • assumeUtc — reinterpret a naive (offset-less) date as UTC before comparing. Use when the value came from a server/DB that stores UTC; a value already flagged UTC is left as-is. • justNowSeconds — anything more recent than this reads as "just now".

Implementation

static String timeAgo(
  DateTime date, {
  DateTime? now,
  bool assumeUtc = false,
  int justNowSeconds = 10,
  TimeAgoLabels? labels,
}) {
  final l = labels ?? TimeAgoLabels.enShort();
  var d = date;
  if (assumeUtc && !d.isUtc) {
    d = DateTime.utc(d.year, d.month, d.day, d.hour, d.minute, d.second,
        d.millisecond, d.microsecond);
  }
  var diff = (now ?? DateTime.now()).difference(d);
  if (diff.isNegative) diff = Duration.zero; // clock skew / future → "just now"

  final s = diff.inSeconds;
  if (s < justNowSeconds) return l.justNow;
  if (s < 60) return l.format(s, TimeUnit.second);
  final m = diff.inMinutes;
  if (m < 60) return l.format(m, TimeUnit.minute);
  final h = diff.inHours;
  if (h < 24) return l.format(h, TimeUnit.hour);
  final days = diff.inDays;
  if (days < 7) return l.format(days, TimeUnit.day);
  if (days < 30) return l.format(days ~/ 7, TimeUnit.week);
  if (days < 365) return l.format(days ~/ 30, TimeUnit.month);
  return l.format(days ~/ 365, TimeUnit.year);
}