65 lines
2.5 KiB
Dart
65 lines
2.5 KiB
Dart
// Shared, framework-free date helpers.
|
|
//
|
|
// The project intentionally avoids `intl` to keep core utilities usable from
|
|
// pure Dart domain code and tests; these formatters cover the app's display
|
|
// needs with explicit, stable output.
|
|
|
|
const List<String> _monthNames = [
|
|
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
|
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
|
|
];
|
|
|
|
/// Formats [time] as e.g. `Oct 16, 2026 · 2:05 PM`.
|
|
String formatDateTime(DateTime time) {
|
|
final local = time.toLocal();
|
|
final hour12 = local.hour % 12 == 0 ? 12 : local.hour % 12;
|
|
final minute = local.minute.toString().padLeft(2, '0');
|
|
final period = local.hour < 12 ? 'AM' : 'PM';
|
|
return '${_monthNames[local.month - 1]} ${local.day}, ${local.year}'
|
|
' · $hour12:$minute $period';
|
|
}
|
|
|
|
/// Start of the local calendar day containing [time] (`00:00:00.000`).
|
|
DateTime startOfDay(DateTime time) =>
|
|
DateTime(time.year, time.month, time.day);
|
|
|
|
/// End of the local calendar day containing [time] — represented as the
|
|
/// exclusive upper bound `00:00:00.000` of the following day, so half-open
|
|
/// `[startOfDay, endOfDay)` ranges cover the whole day without an off-by-one
|
|
/// at midnight.
|
|
DateTime endOfDay(DateTime time) =>
|
|
DateTime(time.year, time.month, time.day + 1);
|
|
|
|
/// Start of the ISO-8601 calendar week containing [time] — Monday
|
|
/// `00:00:00.000` as a local calendar day. Day arithmetic uses the
|
|
/// [DateTime] constructor (not `Duration`) so daylight-saving transitions
|
|
/// can never shift the result off midnight.
|
|
DateTime startOfWeek(DateTime time) {
|
|
final day = startOfDay(time);
|
|
return DateTime(day.year, day.month, day.day - (day.weekday - DateTime.monday));
|
|
}
|
|
|
|
/// Formats [time] as a short month/day label, e.g. `Oct 16`.
|
|
String formatMonthDay(DateTime time) {
|
|
final local = time.toLocal();
|
|
return '${_monthNames[local.month - 1]} ${local.day}';
|
|
}
|
|
|
|
|
|
/// Formats a coarse elapsed time relative to [now], e.g. `never`, `3 hours
|
|
/// ago`, `5 days ago`.
|
|
String formatRelativeTime(DateTime time, {DateTime? now}) {
|
|
final reference = now ?? DateTime.now();
|
|
final elapsed = reference.difference(time);
|
|
|
|
if (elapsed.isNegative) {
|
|
final ahead = time.difference(reference);
|
|
if (ahead.inDays >= 1) return 'in ${ahead.inDays} day(s)';
|
|
if (ahead.inHours >= 1) return 'in ${ahead.inHours} hour(s)';
|
|
return 'in a moment';
|
|
}
|
|
if (elapsed.inMinutes < 1) return 'just now';
|
|
if (elapsed.inHours < 1) return '${elapsed.inMinutes} minute(s) ago';
|
|
if (elapsed.inDays < 1) return '${elapsed.inHours} hour(s) ago';
|
|
return '${elapsed.inDays} day(s) ago';
|
|
} |