formatRelativeTime function

String formatRelativeTime(
  1. DateTime date, {
  2. DateTime? relativeTo,
  3. String? locale,
  4. bool numeric = false,
})

Formats relative time intervals (e.g. "just now", "5 minutes ago", "in 2 hours", "yesterday") in pure Dart.

Evaluates date relative to relativeTo (which defaults to DateTime.now()). If numeric is true, produces numeric representations like "1 day ago" instead of "yesterday".

Supports the built-in English, French, German, Spanish, Japanese, and Chinese phrasing plus the locale messages listed in supportedRelativeTimeLocales. Unsupported locales fall back to English. The formatter is pure Dart, so server-rendered and browser-rendered text use the same locale messages.

final fiveMinAgo = DateTime.now().subtract(const Duration(minutes: 5));
formatRelativeTime(fiveMinAgo, locale: 'en-US'); // "5 minutes ago"
formatRelativeTime(fiveMinAgo, locale: 'fr-FR'); // "il y a 5 minutes"

See also:

Implementation

String formatRelativeTime(
  DateTime date, {
  DateTime? relativeTo,
  String? locale,
  bool numeric = false,
}) {
  final now = relativeTo ?? DateTime.now();
  final diff = now.difference(date);
  final isPast = !diff.isNegative;
  final absSeconds = diff.inSeconds.abs();
  final loc = (locale ?? BloomI18n.instance.locale.value)
      .replaceAll('_', '-')
      .toLowerCase();
  final lang = loc.split('-').first;

  final messages = _relativeTimeMessages(lang);
  if (messages != null) {
    return _formatRelativeWithMessages(
      messages,
      absSeconds: absSeconds,
      isPast: isPast,
      numeric: numeric,
    );
  }

  if (absSeconds < 45) {
    if (lang == 'fr') return 'à l\'instant';
    if (lang == 'de') return 'gerade eben';
    if (lang == 'es') return 'hace un momento';
    if (lang == 'ja') return 'たった今';
    if (lang == 'zh') return '刚刚';
    return 'just now';
  }

  final minutes = (absSeconds / 60).round();
  if (minutes < 45) {
    return _formatRelativeUnit(minutes, 'minute', isPast, lang);
  }

  final hours = (absSeconds / 3600).round();
  if (hours < 22) {
    return _formatRelativeUnit(hours, 'hour', isPast, lang);
  }

  final days = (absSeconds / 86400).round();
  if (days == 1 && !numeric) {
    if (isPast) {
      if (lang == 'fr') return 'hier';
      if (lang == 'de') return 'gestern';
      if (lang == 'es') return 'ayer';
      if (lang == 'ja') return '昨日';
      if (lang == 'zh') return '昨天';
      return 'yesterday';
    } else {
      if (lang == 'fr') return 'demain';
      if (lang == 'de') return 'morgen';
      if (lang == 'es') return 'mañana';
      if (lang == 'ja') return '明日';
      if (lang == 'zh') return '明天';
      return 'tomorrow';
    }
  }

  if (days < 26) {
    return _formatRelativeUnit(days, 'day', isPast, lang);
  }

  final months = (days / 30).round();
  if (months < 11) {
    return _formatRelativeUnit(months, 'month', isPast, lang);
  }

  final years = (days / 365).round();
  return _formatRelativeUnit(years, 'year', isPast, lang);
}