formatRelativeTime function
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:
- BloomDateTimeClientI18n.toLocalizedRelativeTime, extension method on DateTime.
- localizedRelativeTime, top-level convenience shorthand.
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);
}