flutter_relative_time 0.2.0
flutter_relative_time: ^0.2.0 copied to clipboard
Human-friendly past and future DateTime formatting with localized timeago and calendar labels.
flutter_relative_time #
flutter_relative_time formats nullable DateTime values into short,
localized labels for product interfaces.
It supports past dates, future dates, very recent values, calendar labels,
custom messages, custom locales, and convenient DateTime extensions.
The package core is Dart-only. It works in Flutter apps, command-line tools, server-side Dart, and tests without requiring Flutter-specific APIs.
Default behavior #
The default toRelativeTime() strategy is optimized for feeds, notifications,
orders, chats, and activity lists:
final now = DateTime(2026, 8, 3, 12); // Monday
DateTime(2026, 8, 3, 11, 55).toRelativeTime(
locale: 'es_ES',
options: RelativeTimeFormatOptions(now: now),
);
// hace 5 min
DateTime(2026, 8, 2, 18, 45).toRelativeTime(
locale: 'es-ES',
options: RelativeTimeFormatOptions(now: now),
);
// 18:45
DateTime(2026, 8, 1, 13, 20).toRelativeTime(
locale: 'es_ES',
options: RelativeTimeFormatOptions(now: now),
);
// 1 ago, 13:20
DateTime(2026, 8, 5, 9, 15).toRelativeTime(
locale: 'es_ES',
options: RelativeTimeFormatOptions(now: now),
);
// mié, 09:15
In plain language:
- dates from today use timeago-style labels;
- yesterday shows the time;
- tomorrow shows a localized tomorrow + time label;
- dates in the same calendar week show weekday + time;
- dates in the same month show month/day + time;
- dates from the same year show month/day + time;
- older dates include the year;
- very close values across midnight can still use timeago labels.
Future dates use the same product-friendly logic:
DateTime(2026, 8, 4, 10).toRelativeTime(
locale: 'es_ES',
options: RelativeTimeFormatOptions(now: now),
);
// mañana, 10:00
Weekday labels are based on the real calendar week, not just on distance. For example, if today is Monday, the previous Saturday is formatted as a date:
final now = DateTime(2026, 8, 3, 12); // Monday
DateTime(2026, 8, 1, 13, 20).toRelativeTime(
locale: 'es_ES',
options: RelativeTimeFormatOptions(now: now),
);
// 1 ago, 13:20
Locale parameter #
Use locale: for both language codes and locale strings.
Accepted examples:
value.toRelativeTime(locale: 'es');
value.toRelativeTime(locale: 'es_ES');
value.toRelativeTime(locale: 'es-ES');
value.toRelativeTime(locale: 'pt_BR');
value.toRelativeTime(locale: 'en_GB');
The package normalizes hyphenated and underscored formats internally. Region-aware locales are preserved for date formatting and clock selection.
Formatting modes #
RelativeTimeFormatter.format(value, locale: 'es');
RelativeTimeFormatter.timeAgo(value, locale: 'pt_BR');
RelativeTimeFormatter.calendar(value, locale: 'en_GB');
RelativeTimeFormatMode.smart: default product behavior.RelativeTimeFormatMode.timeAgo: always relative labels likehace 5 min,in 2h, orhá 3 h.RelativeTimeFormatMode.calendar: calendar labels liketoday, 5:03 AM,tomorrow, 05:03, ormañana, 05:03.
Nullable dates #
The extension works on nullable values. If the value is null, it returns the
localized unknown label.
final DateTime? deliveredAt = order.deliveredAt;
final label = deliveredAt.toRelativeTime(locale: 'es_ES');
Timeago-only labels #
final startsAt = DateTime(2026, 8, 2, 14);
final label = startsAt.toTimeAgo(
locale: 'en_US',
options: RelativeTimeFormatOptions.timeAgo(now: now),
);
// in 2h
Very recent values are controlled by justNowThreshold:
final sentAt = DateTime(2026, 8, 2, 11, 59, 50);
final label = sentAt.toTimeAgo(
locale: 'en_US',
options: RelativeTimeFormatOptions.timeAgo(
now: now,
justNowThreshold: Duration(seconds: 30),
),
);
// just now
The same rule applies to near-future values:
final retryAt = DateTime(2026, 8, 2, 12, 0, 10);
final label = retryAt.toTimeAgo(
locale: 'en_US',
options: RelativeTimeFormatOptions.timeAgo(now: now),
);
// soon
Calendar-only labels #
Use toRelativeCalendar() when you always want calendar wording instead of the
smart feed-style behavior.
final opensAt = DateTime(2026, 8, 3, 10);
final label = opensAt.toRelativeCalendar(
locale: 'es_ES',
options: RelativeTimeFormatOptions.calendar(now: now),
);
// mañana, 10:00
Clock format #
By default, clockFormat is RelativeTimeClockFormat.auto.
const options = RelativeTimeFormatOptions.calendar(
clockFormat: RelativeTimeClockFormat.auto,
);
auto chooses 12h or 24h from the effective locale. You can also force the
clock per call:
const twentyFourHour = RelativeTimeFormatOptions.calendar(
clockFormat: RelativeTimeClockFormat.twentyFourHour,
);
const twelveHour = RelativeTimeFormatOptions.calendar(
clockFormat: RelativeTimeClockFormat.twelveHour,
);
When 24h is selected, the hour always uses two digits:
tomorrow, 05:03
mañana, 05:03
Custom date and time patterns #
The default date groups are usually enough, but every date/time piece can be overridden with ICU patterns.
final label = DateTime(2026, 8, 20, 9, 15).toRelativeTime(
locale: 'en_US',
options: RelativeTimeFormatOptions(
now: DateTime(2026, 8, 3, 12),
timePattern: 'HH:mm',
weekdayDatePattern: 'EEEE',
sameMonthDatePattern: 'dd/MM',
sameYearDatePattern: 'dd MMM',
fullDatePattern: 'dd/MM/y',
),
);
// 20/08, 09:15
Available pattern options:
timePattern: formats the time.weekdayDatePattern: formats dates in the same calendar week.sameMonthDatePattern: formats dates in the same month.sameYearDatePattern: formats dates in the same year.fullDatePattern: formats dates from a different year.
Week, yesterday, and rounding behavior #
Smart formatting can be tuned for different product surfaces without changing the formatter code.
Use weekStart when your product groups weeks differently:
final label = DateTime(2026, 8, 2, 13, 20).toRelativeTime(
locale: 'en_US',
options: RelativeTimeFormatOptions(
now: DateTime(2026, 8, 5, 12), // Wednesday
weekStart: RelativeTimeWeekStart.sunday,
),
);
// Sun, 1:20 PM
Use yesterdayStyle when you prefer explicit yesterday labels instead of the
default chat-style time-only label:
final label = DateTime(2026, 8, 2, 18, 45).toRelativeTime(
locale: 'en_US',
options: RelativeTimeFormatOptions(
now: DateTime(2026, 8, 3, 12),
yesterdayStyle: RelativeTimeYesterdayStyle.yesterdayAt,
),
);
// yesterday, 6:45 PM
Use rounding when converting long durations into weeks, months, and years:
final label = DateTime(2026, 7, 23, 12).toTimeAgo(
locale: 'en',
options: RelativeTimeFormatOptions.timeAgo(
now: DateTime(2026, 8, 2, 12),
rounding: RelativeTimeRounding.ceil,
),
);
// 2w ago
Keep business text outside the formatter #
The package formats only the temporal part. Product-specific verbs should stay in the app layer.
final date = nextOpeningAt.toRelativeTime(locale: context.locale.toString());
final text = 'Opens $date';
This keeps copy flexible for phrases such as Opens, Closes, Starts,
Expires, or any other product wording.
Custom messages #
Override one or more message templates for a single call:
final label = value.toTimeAgo(
locale: 'es_ES',
options: const RelativeTimeFormatOptions.timeAgo(
overrides: {
'relative-time.just-now': 'ahora mismo',
'relative-time.soon': 'en breve',
},
),
);
Custom locales #
You can add or replace a locale without changing the package source:
final label = value.toRelativeCalendar(
locale: 'nl_NL',
options: RelativeTimeFormatOptions.calendar(
locales: {
'nl_NL': RelativeTimeLocaleMessages(
locale: 'nl_NL',
values: {
'relative-time.today-at': 'vandaag, {time}',
'relative-time.tomorrow-at': 'morgen, {time}',
'relative-time.yesterday-at': 'gisteren, {time}',
'relative-time.weekday-at': '{weekday}, {time}',
'relative-time.date-at': '{date}, {time}',
'relative-time.unknown': 'onbekend',
},
),
},
),
);
Built-in languages #
- Arabic:
ar - German:
de - English:
en - Spanish:
es - French:
fr - Italian:
it - Portuguese:
pt - Russian:
ru - Chinese:
zh
Example app #
cd example
flutter run
License #
MIT License.