1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'calendar_date_picker.dart';
6/// @docImport 'date_picker.dart';
7/// @docImport 'text_field.dart';
8library;
9
10import 'package:flutter/foundation.dart';
11
12import 'material_localizations.dart';
13
14/// Utility functions for working with dates.
15abstract final class DateUtils {
16 /// Returns a [DateTime] with the date of the original, but time set to
17 /// midnight.
18 static DateTime dateOnly(DateTime date) {
19 return DateTime(date.year, date.month, date.day);
20 }
21
22 /// Returns a [DateTimeRange] with the dates of the original, but with times
23 /// set to midnight.
24 ///
25 /// See also:
26 /// * [dateOnly], which does the same thing for a single date.
27 static DateTimeRange datesOnly(DateTimeRange range) {
28 return DateTimeRange(start: dateOnly(range.start), end: dateOnly(range.end));
29 }
30
31 /// Returns true if the two [DateTime] objects have the same day, month, and
32 /// year, or are both null.
33 static bool isSameDay(DateTime? dateA, DateTime? dateB) {
34 return
35 dateA?.year == dateB?.year &&
36 dateA?.month == dateB?.month &&
37 dateA?.day == dateB?.day;
38 }
39
40 /// Returns true if the two [DateTime] objects have the same month and
41 /// year, or are both null.
42 static bool isSameMonth(DateTime? dateA, DateTime? dateB) {
43 return
44 dateA?.year == dateB?.year &&
45 dateA?.month == dateB?.month;
46 }
47
48 /// Determines the number of months between two [DateTime] objects.
49 ///
50 /// For example:
51 ///
52 /// ```dart
53 /// DateTime date1 = DateTime(2019, 6, 15);
54 /// DateTime date2 = DateTime(2020, 1, 15);
55 /// int delta = DateUtils.monthDelta(date1, date2);
56 /// ```
57 ///
58 /// The value for `delta` would be `7`.
59 static int monthDelta(DateTime startDate, DateTime endDate) {
60 return (endDate.year - startDate.year) * 12 + endDate.month - startDate.month;
61 }
62
63 /// Returns a [DateTime] that is [monthDate] with the added number
64 /// of months and the day set to 1 and time set to midnight.
65 ///
66 /// For example:
67 ///
68 /// ```dart
69 /// DateTime date = DateTime(2019, 1, 15);
70 /// DateTime futureDate = DateUtils.addMonthsToMonthDate(date, 3);
71 /// ```
72 ///
73 /// `date` would be January 15, 2019.
74 /// `futureDate` would be April 1, 2019 since it adds 3 months.
75 static DateTime addMonthsToMonthDate(DateTime monthDate, int monthsToAdd) {
76 return DateTime(monthDate.year, monthDate.month + monthsToAdd);
77 }
78
79 /// Returns a [DateTime] with the added number of days and time set to
80 /// midnight.
81 static DateTime addDaysToDate(DateTime date, int days) {
82 return DateTime(date.year, date.month, date.day + days);
83 }
84
85 /// Computes the offset from the first day of the week that the first day of
86 /// the [month] falls on.
87 ///
88 /// For example, September 1, 2017 falls on a Friday, which in the calendar
89 /// localized for United States English appears as:
90 ///
91 /// S M T W T F S
92 /// _ _ _ _ _ 1 2
93 ///
94 /// The offset for the first day of the months is the number of leading blanks
95 /// in the calendar, i.e. 5.
96 ///
97 /// The same date localized for the Russian calendar has a different offset,
98 /// because the first day of week is Monday rather than Sunday:
99 ///
100 /// M T W T F S S
101 /// _ _ _ _ 1 2 3
102 ///
103 /// So the offset is 4, rather than 5.
104 ///
105 /// This code consolidates the following:
106 ///
107 /// - [DateTime.weekday] provides a 1-based index into days of week, with 1
108 /// falling on Monday.
109 /// - [MaterialLocalizations.firstDayOfWeekIndex] provides a 0-based index
110 /// into the [MaterialLocalizations.narrowWeekdays] list.
111 /// - [MaterialLocalizations.narrowWeekdays] list provides localized names of
112 /// days of week, always starting with Sunday and ending with Saturday.
113 static int firstDayOffset(int year, int month, MaterialLocalizations localizations) {
114 // 0-based day of week for the month and year, with 0 representing Monday.
115 final int weekdayFromMonday = DateTime(year, month).weekday - 1;
116
117 // 0-based start of week depending on the locale, with 0 representing Sunday.
118 int firstDayOfWeekIndex = localizations.firstDayOfWeekIndex;
119
120 // firstDayOfWeekIndex recomputed to be Monday-based, in order to compare with
121 // weekdayFromMonday.
122 firstDayOfWeekIndex = (firstDayOfWeekIndex - 1) % 7;
123
124 // Number of days between the first day of week appearing on the calendar,
125 // and the day corresponding to the first of the month.
126 return (weekdayFromMonday - firstDayOfWeekIndex) % 7;
127 }
128
129 /// Returns the number of days in a month, according to the proleptic
130 /// Gregorian calendar.
131 ///
132 /// This applies the leap year logic introduced by the Gregorian reforms of
133 /// 1582. It will not give valid results for dates prior to that time.
134 static int getDaysInMonth(int year, int month) {
135 if (month == DateTime.february) {
136 final bool isLeapYear = (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0);
137 return isLeapYear ? 29 : 28;
138 }
139 const List<int> daysInMonth = <int>[31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
140 return daysInMonth[month - 1];
141 }
142}
143
144/// Mode of date entry method for the date picker dialog.
145///
146/// In [calendar] mode, a calendar grid is displayed and the user taps the
147/// day they wish to select. In [input] mode, a [TextField] is displayed and
148/// the user types in the date they wish to select.
149///
150/// [calendarOnly] and [inputOnly] are variants of the above that don't
151/// allow the user to change to the mode.
152///
153/// See also:
154///
155/// * [showDatePicker] and [showDateRangePicker], which use this to control
156/// the initial entry mode of their dialogs.
157enum DatePickerEntryMode {
158 /// User picks a date from calendar grid. Can switch to [input] by activating
159 /// a mode button in the dialog.
160 calendar,
161
162 /// User can input the date by typing it into a text field.
163 ///
164 /// Can switch to [calendar] by activating a mode button in the dialog.
165 input,
166
167 /// User can only pick a date from calendar grid.
168 ///
169 /// There is no user interface to switch to another mode.
170 calendarOnly,
171
172 /// User can only input the date by typing it into a text field.
173 ///
174 /// There is no user interface to switch to another mode.
175 inputOnly,
176}
177
178/// Initial display of a calendar date picker.
179///
180/// Either a grid of available years or a monthly calendar.
181///
182/// See also:
183///
184/// * [showDatePicker], which shows a dialog that contains a Material Design
185/// date picker.
186/// * [CalendarDatePicker], widget which implements the Material Design date picker.
187enum DatePickerMode {
188 /// Choosing a month and day.
189 day,
190
191 /// Choosing a year.
192 year,
193}
194
195/// Signature for predicating dates for enabled date selections.
196///
197/// See [showDatePicker], which has a [SelectableDayPredicate] parameter used
198/// to specify allowable days in the date picker.
199typedef SelectableDayPredicate = bool Function(DateTime day);
200
201/// Encapsulates a start and end [DateTime] that represent the range of dates.
202///
203/// The range includes the [start] and [end] dates. The [start] and [end] dates
204/// may be equal to indicate a date range of a single day. The [start] date must
205/// not be after the [end] date.
206///
207/// See also:
208/// * [showDateRangePicker], which displays a dialog that allows the user to
209/// select a date range.
210@immutable
211class DateTimeRange {
212 /// Creates a date range for the given start and end [DateTime].
213 DateTimeRange({
214 required this.start,
215 required this.end,
216 }) : assert(!start.isAfter(end));
217
218 /// The start of the range of dates.
219 final DateTime start;
220
221 /// The end of the range of dates.
222 final DateTime end;
223
224 /// Returns a [Duration] of the time between [start] and [end].
225 ///
226 /// See [DateTime.difference] for more details.
227 Duration get duration => end.difference(start);
228
229 @override
230 bool operator ==(Object other) {
231 if (other.runtimeType != runtimeType) {
232 return false;
233 }
234 return other is DateTimeRange
235 && other.start == start
236 && other.end == end;
237 }
238
239 @override
240 int get hashCode => Object.hash(start, end);
241
242 @override
243 String toString() => '$start - $end';
244}
245

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com