// 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 _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'; }