123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- /// Bunch of useful functions for date pickers.
- class DatePickerUtils {
- /// Returns if two objects have same year, month and day.
- /// Time doesn't matter.
- static bool sameDate(DateTime dateTimeOne, DateTime dateTimeTwo) =>
- dateTimeOne.year == dateTimeTwo.year &&
- dateTimeOne.month == dateTimeTwo.month &&
- dateTimeOne.day == dateTimeTwo.day;
- /// Returns if two objects have same year and month.
- /// Day and time don't matter/
- static bool sameMonth(DateTime dateTimeOne, DateTime dateTimeTwo) =>
- dateTimeOne.year == dateTimeTwo.year &&
- dateTimeOne.month == dateTimeTwo.month;
- // Do not use this directly - call getDaysInMonth instead.
- static const List<int> _daysInMonth = <int>[
- 31,
- -1,
- 31,
- 30,
- 31,
- 30,
- 31,
- 31,
- 30,
- 31,
- 30,
- 31
- ];
- /// Returns the number of days in a month, according to the proleptic
- /// Gregorian calendar.
- ///
- /// This applies the leap year logic introduced by the Gregorian reforms of
- /// 1582. It will not give valid results for dates prior to that time.
- static int getDaysInMonth(int year, int month) {
- if (month == DateTime.february) {
- final bool isLeapYear =
- (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0);
- return isLeapYear ? 29 : 28;
- }
- return _daysInMonth[month - 1];
- }
- /// Returns number of months between [startDate] and [endDate]
- static int monthDelta(DateTime startDate, DateTime endDate) =>
- (endDate.year - startDate.year) * 12 + endDate.month - startDate.month;
- /// Add months to a month truncated date.
- static DateTime addMonthsToMonthDate(DateTime monthDate, int monthsToAdd) =>
- // year is switched automatically if new month > 12
- DateTime(monthDate.year, monthDate.month + monthsToAdd);
- /// Returns number of years between [startDate] and [endDate]
- static int yearDelta(DateTime startDate, DateTime endDate) =>
- endDate.year - startDate.year;
- /// Returns start of the first day of the week with given day.
- ///
- /// Start of the week calculated using firstDayIndex which is int from 0 to 6
- /// where 0 points to Sunday and 6 points to Saturday.
- /// (according to MaterialLocalization.firstDayIfWeekIndex)
- static DateTime getFirstDayOfWeek(DateTime day, int firstDayIndex) {
- // from 1 to 7 where 1 points to Monday and 7 points to Sunday
- int weekday = day.weekday;
- // to match weekdays where Sunday is 7 not 0
- if (firstDayIndex == 0) firstDayIndex = 7;
- int diff = weekday - firstDayIndex;
- if (diff < 0) diff = 7 + diff;
- DateTime firstDayOfWeek = DateTime(
- day.year,
- day.month,
- day.day - diff,
- day.hour,
- day.minute,
- day.second,
- );
- firstDayOfWeek = startOfTheDay(firstDayOfWeek);
- return firstDayOfWeek;
- }
- /// Returns end of the last day of the week with given day.
- ///
- /// Start of the week calculated using firstDayIndex which is int from 0 to 6
- /// where 0 points to Sunday and 6 points to Saturday.
- /// (according to MaterialLocalization.firstDayIfWeekIndex)
- static DateTime getLastDayOfWeek(DateTime day, int firstDayIndex) {
- // from 1 to 7 where 1 points to Monday and 7 points to Sunday
- int weekday = day.weekday;
- // to match weekdays where Sunday is 7 not 0
- if (firstDayIndex == 0) firstDayIndex = 7;
- int lastDayIndex = firstDayIndex - 1;
- if (lastDayIndex == 0) lastDayIndex = 7;
- int diff = lastDayIndex - weekday;
- if (diff < 0) diff = 7 + diff;
- DateTime lastDayOfWeek = DateTime(
- day.year,
- day.month,
- day.day + diff,
- day.hour,
- day.minute,
- day.second,
- );
- lastDayOfWeek = endOfTheDay(lastDayOfWeek);
- return lastDayOfWeek;
- }
- /// Returns end of the given day.
- ///
- /// End time is 1 millisecond before start of the next day.
- static DateTime endOfTheDay(DateTime date) {
- DateTime tomorrowStart = DateTime(date.year, date.month, date.day + 1);
- DateTime result = tomorrowStart.subtract(const Duration(milliseconds: 1));
- return result;
- }
- /// Returns start of the given day.
- ///
- /// Start time is 00:00:00.
- static DateTime startOfTheDay(DateTime date) =>
- DateTime(date.year, date.month, date.day);
- /// Returns first shown date for the [curMonth].
- ///
- /// First shown date is not always 1st day of the [curMonth].
- /// It can be day from previous month if [showEndOfPrevMonth] is true.
- ///
- /// If [showEndOfPrevMonth] is true empty day cells before 1st [curMonth]
- /// are filled with days from the previous month.
- static DateTime firstShownDate({
- required DateTime curMonth,
- required bool showEndOfPrevMonth,
- required int firstDayOfWeekFromSunday,
- }) {
- DateTime result = DateTime(curMonth.year, curMonth.month, 1);
- if (showEndOfPrevMonth) {
- int firstDayOffset = computeFirstDayOffset(
- curMonth.year, curMonth.month, firstDayOfWeekFromSunday);
- if (firstDayOffset == 0) return result;
- int prevMonth = curMonth.month - 1;
- if (prevMonth < 1) prevMonth = 12;
- int prevYear = prevMonth == 12 ? curMonth.year - 1 : curMonth.year;
- int daysInPrevMonth = getDaysInMonth(prevYear, prevMonth);
- int firstShownDay = daysInPrevMonth - firstDayOffset + 1;
- result = DateTime(prevYear, prevMonth, firstShownDay);
- }
- return result;
- }
- /// Returns last shown date for the [curMonth].
- ///
- /// Last shown date is not always last day of the [curMonth].
- /// It can be day from next month if [showStartNextMonth] is true.
- ///
- /// If [showStartNextMonth] is true empty day cells after last day
- /// of [curMonth] are filled with days from the next month.
- static DateTime lastShownDate({
- required DateTime curMonth,
- required bool showStartNextMonth,
- required int firstDayOfWeekFromSunday,
- }) {
- int daysInCurMonth = getDaysInMonth(curMonth.year, curMonth.month);
- DateTime result = DateTime(curMonth.year, curMonth.month, daysInCurMonth);
- if (showStartNextMonth) {
- int firstDayOffset = computeFirstDayOffset(
- curMonth.year, curMonth.month, firstDayOfWeekFromSunday);
- int totalDays = firstDayOffset + daysInCurMonth;
- int trailingDaysCount = 7 - totalDays % 7;
- bool fullWeekTrailing = trailingDaysCount == 7;
- if (fullWeekTrailing) return result;
- result = DateTime(curMonth.year, curMonth.month + 1, trailingDaysCount);
- }
- return result;
- }
- /// Computes the offset from the first day of week that the first day of the
- /// [month] falls on.
- ///
- /// For example, September 1, 2017 falls on a Friday, which in the calendar
- /// localized for United States English appears as:
- ///
- /// ```
- /// S M T W T F S
- /// _ _ _ _ _ 1 2
- /// ```
- ///
- /// The offset for the first day of the months is the number of leading blanks
- /// in the calendar, i.e. 5.
- ///
- /// The same date localized for the Russian calendar has a different offset,
- /// because the first day of week is Monday rather than Sunday:
- ///
- /// ```
- /// M T W T F S S
- /// _ _ _ _ 1 2 3
- /// ```
- ///
- /// So the offset is 4, rather than 5.
- ///
- /// This code consolidates the following:
- ///
- /// - [DateTime.weekday] provides a 1-based index into days of week, with 1
- /// falling on Monday.
- /// - MaterialLocalizations.firstDayOfWeekIndex provides a 0-based index
- /// into the MaterialLocalizations.narrowWeekdays list.
- /// - MaterialLocalizations.narrowWeekdays list provides localized names of
- /// days of week, always starting with Sunday and ending with Saturday.
- static int computeFirstDayOffset(
- int year, int month, int firstDayOfWeekFromSunday) {
- // 0-based day of week, with 0 representing Monday.
- final int weekdayFromMonday = DateTime(year, month).weekday - 1;
- // firstDayOfWeekFromSunday recomputed to be Monday-based
- final int firstDayOfWeekFromMonday = (firstDayOfWeekFromSunday - 1) % 7;
- // Number of days between the first day of week appearing on the calendar,
- // and the day corresponding to the 1-st of the month.
- return (weekdayFromMonday - firstDayOfWeekFromMonday) % 7;
- }
- /// Returns earliest [DateTime] from the list.
- ///
- /// [dates] must not be null.
- /// In case it is null, [ArgumentError] will be thrown.
- static DateTime getEarliestFromList(List<DateTime> dates) {
- ArgumentError.checkNotNull(dates, "dates");
- return dates.fold(dates[0], getEarliest);
- }
- /// Returns latest [DateTime] from the list.
- ///
- /// [dates] must not be null.
- /// In case it is null, [ArgumentError] will be thrown.
- static DateTime getLatestFromList(List<DateTime> dates) {
- ArgumentError.checkNotNull(dates, "dates");
- return dates.fold(dates[0], getLatest);
- }
- /// Returns earliest [DateTime] from two.
- ///
- /// If two [DateTime]s is the same moment first ([a]) will be return.
- static DateTime getEarliest(DateTime a, DateTime b) => a.isBefore(b) ? a : b;
- /// Returns latest [DateTime] from two.
- ///
- /// If two [DateTime]s is the same moment first ([a]) will be return.
- static DateTime getLatest(DateTime a, DateTime b) => a.isAfter(b) ? a : b;
- }
|