|
import 'package:clock/clock.dart'; |
|
import 'package:your_awesome_app/l10n/l10n.dart'; |
|
import 'package:intl/intl.dart'; |
|
|
|
/// DateTime extension containing various formatting and date calculation |
|
/// helpers |
|
extension DateTimeHelper on DateTime { |
|
/// Formats date as a section header title with special handling for today/yesterday |
|
/// and different formats for current year vs previous years |
|
/// |
|
/// Example: |
|
/// - Today => "TODAY" |
|
/// - Yesterday => "YESTERDAY" |
|
/// - Same year date => "WEDNESDAY, JAN 3" |
|
/// - Cross-year date => "WEDNESDAY, JAN 3 2023" |
|
String formattedSectionTitle(AppLocalizations l10n) { |
|
final today = clock.now(); |
|
final yesterday = today.yesterday; |
|
|
|
final String title; |
|
if (isBetween(today.startOfDay, today.endOfDay)) { |
|
title = l10n.today; |
|
} else if (isBetween(yesterday.startOfDay, yesterday.endOfDay)) { |
|
title = l10n.yesterday; |
|
} else if (year == today.year) { |
|
final dateFormat = DateFormat('EEEE, MMM d'); |
|
title = dateFormat.format(this); |
|
} else { |
|
final dateFormat = DateFormat('EEEE, MMM d y'); |
|
title = dateFormat.format(this); |
|
} |
|
|
|
return title.toUpperCase(); |
|
} |
|
|
|
/// Formats time in lowercase am/pm format |
|
/// |
|
/// Example: 21:30 => "9:30 pm" |
|
String formattedTimeLowerCaseAmPm() { |
|
final tripFormat = DateFormat('h:mm a'); |
|
return tripFormat.format(this).toLowerCase(); |
|
} |
|
|
|
/// Formats elapsed time since this date |
|
/// |
|
/// Example: "5 minutes ago", "3 hours ago", "2 days ago" |
|
String formatElapsedTime({ |
|
required AppLocalizations l10n, |
|
}) { |
|
final now = clock.now(); |
|
final timeSince = now.difference(this); |
|
var elapsedTime = ''; |
|
if (timeSince.inMinutes < 1) { |
|
return l10n.justNow; |
|
} else if (timeSince.inMinutes < 60) { |
|
elapsedTime = l10n.timeDurationMinutes(timeSince.inMinutes); |
|
} else if (timeSince.inHours < 24) { |
|
elapsedTime = l10n.timeDurationHours(timeSince.inHours); |
|
} else if (timeSince.inDays < 7) { |
|
elapsedTime = l10n.timeDurationDays(timeSince.inDays); |
|
} else { |
|
elapsedTime = l10n.timeDurationWeeks(timeSince.inDays ~/ 7); |
|
} |
|
return l10n.timeAgo(elapsedTime); |
|
} |
|
|
|
/// Formats refresh time status with different phrasing than regular elapsed |
|
/// time |
|
/// |
|
/// Example: "Refreshed 2 hours ago", "Refreshed 3 months ago" |
|
String formattedRefreshedTimeStatus(AppLocalizations l10n) { |
|
final localTime = toLocal(); |
|
final timeSince = clock.now().difference(localTime); |
|
|
|
if (timeSince.inHours < 1) { |
|
return l10n.refreshedTimeInMinutes(timeSince.inMinutes); |
|
} else if (timeSince.inHours < 24) { |
|
return l10n.refreshedTimeInHours(timeSince.inHours); |
|
} else if (timeSince.inDays < 14) { |
|
return l10n.refreshedTimeInDays(timeSince.inDays); |
|
} else if (timeSince.inDays < daysInMonth) { |
|
return l10n.refreshedTimeInWeeks((timeSince.inDays / 7).round()); |
|
} else if (timeSince.inDays > 365) { |
|
return l10n.refreshedTimeInYears(timeSince.inDays ~/ 365); |
|
} else { |
|
var timestamp = localTime; |
|
var daysRemaining = |
|
timeSince.inDays + localTime.daysInMonth - localTime.day; |
|
var months = 0; |
|
|
|
while (daysRemaining > 0) { |
|
final daysInMonth = timestamp.daysInMonth; |
|
if (daysRemaining < daysInMonth) { |
|
break; |
|
} |
|
|
|
months += 1; |
|
daysRemaining -= daysInMonth; |
|
timestamp = DateTime( |
|
timestamp.year, |
|
timestamp.month + 1, |
|
); |
|
} |
|
return l10n.refreshedTimeInMonths(months); |
|
} |
|
} |
|
|
|
/// Formats date in MM/dd/yyyy format |
|
/// Example: 2023-01-05 => "01/05/2023" |
|
String formattedMMddyyyy() { |
|
final dateFormat = DateFormat('MM/dd/yyyy'); |
|
return dateFormat.format(this); |
|
} |
|
|
|
/// Formats date in MM/dd format |
|
/// Example: 2023-01-05 => "01/05" |
|
String formattedMMdd() { |
|
final dateFormat = DateFormat('MM/dd'); |
|
return dateFormat.format(this); |
|
} |
|
|
|
/// Formats date in abbreviated month format |
|
/// Example: 2023-01-20 => "Jan 20, 2023" |
|
String formattedMMMdy() { |
|
final dateFormat = DateFormat('MMM d, y'); |
|
return dateFormat.format(this); |
|
} |
|
|
|
/// Formats date in full month format |
|
/// Example: 2023-01-20 => "January 20, 2023" |
|
String formattedMMMMdy() { |
|
final dateFormat = DateFormat('MMMM d, y'); |
|
return dateFormat.format(this); |
|
} |
|
|
|
/// Formats date with full weekday and month name |
|
/// Example: 2023-05-20 => "Saturday, May 20" |
|
String formattedEEEEMMMMd() { |
|
final dateFormat = DateFormat('EEEE, MMMM d'); |
|
return dateFormat.format(this); |
|
} |
|
|
|
/// Gets DateTime for yesterday at midnight |
|
/// Example: 2023-01-05 14:30 => 2023-01-04 00:00 |
|
DateTime get yesterday { |
|
return DateTime( |
|
year, |
|
month, |
|
day - 1, |
|
); |
|
} |
|
|
|
/// Gets DateTime for start of current day (midnight) |
|
/// Example: 2023-01-05 14:30 => 2023-01-05 00:00 |
|
DateTime get startOfDay { |
|
return DateTime( |
|
year, |
|
month, |
|
day, |
|
); |
|
} |
|
|
|
/// Gets DateTime for end of current day (23:59:59.999) |
|
/// Example: 2023-01-05 14:30 => 2023-01-05 23:59:59.999 |
|
DateTime get endOfDay { |
|
return DateTime( |
|
year, |
|
month, |
|
day, |
|
23, |
|
59, |
|
59, |
|
999, |
|
); |
|
} |
|
|
|
/// Checks if this date is between two other dates (inclusive) |
|
/// Example: 2023-06-15.isBetween(2023-06-01, 2023-06-30) => true |
|
bool isBetween(DateTime start, DateTime end) { |
|
return (isBefore(end) || this == end) && (isAfter(start) || this == start); |
|
} |
|
|
|
/// Checks if two dates occur on the same calendar day |
|
/// Example: 2023-01-05 14:30.isSameDay(2023-01-05 09:00) => true |
|
bool isSameDay(DateTime? other) { |
|
return year == other?.year && month == other?.month && day == other?.day; |
|
} |
|
|
|
/// Gets number of days in the current month |
|
/// Example: February 2024 => 29 (leap year) |
|
int get daysInMonth { |
|
return DateTime( |
|
year, |
|
month + 1, |
|
0, |
|
).day; |
|
} |
|
} |