mirror of https://github.com/AMT-Cheif/drift.git
Merge pull request #1921 from North101/develop
More DateTime functions and changes
This commit is contained in:
commit
38f343c5e7
|
@ -39,28 +39,66 @@ extension DateTimeExpressions on Expression<DateTime?> {
|
|||
Expression<int?> get second => _StrftimeSingleFieldExpression('%S', this);
|
||||
|
||||
/// Formats this datetime in the format `year-month-day`.
|
||||
Expression<String?> get date {
|
||||
return FunctionCallExpression(
|
||||
'DATE',
|
||||
[this, const Constant<String>('unixepoch')],
|
||||
);
|
||||
}
|
||||
Expression<String?> get date => FunctionCallExpression(
|
||||
'DATE', [this, const DateTimeModifier._unixEpoch()]);
|
||||
|
||||
/// Formats this datetime in the format `hour:minute:second`.
|
||||
Expression<String?> get time {
|
||||
return FunctionCallExpression(
|
||||
'TIME',
|
||||
[this, const Constant<String>('unixepoch')],
|
||||
);
|
||||
}
|
||||
Expression<String?> get time => FunctionCallExpression(
|
||||
'TIME', [this, const DateTimeModifier._unixEpoch()]);
|
||||
|
||||
/// Formats this datetime in the format `year-month-day hour:minute:second`.
|
||||
Expression<String?> get datetime {
|
||||
return FunctionCallExpression(
|
||||
'DATETIME',
|
||||
[this, const Constant<String>('unixepoch')],
|
||||
);
|
||||
}
|
||||
Expression<String?> get datetime => FunctionCallExpression(
|
||||
'DATETIME', [this, const DateTimeModifier._unixEpoch()]);
|
||||
|
||||
/// Formats this datetime as a unix timestamp - the number of seconds since
|
||||
/// 1970-01-01 00:00:00 UTC. The unixepoch() always returns an integer, even
|
||||
/// if the input time-value has millisecond precision.
|
||||
Expression<int?> get unixepoch => FunctionCallExpression(
|
||||
'UNIXEPOCH', [this, const DateTimeModifier._unixEpoch()]);
|
||||
|
||||
/// Formats this datetime in the Julian day format - a fractional number of
|
||||
/// days since noon in Greenwich on November 24, 4714 B.C.
|
||||
Expression<double?> get julianday => FunctionCallExpression(
|
||||
'JULIANDAY', [this, const DateTimeModifier._unixEpoch()]);
|
||||
|
||||
/// Formats this datetime according to the format string specified as the
|
||||
/// first argument. The format string supports the most common substitutions
|
||||
/// found in the strftime() function from the standard C library plus two new
|
||||
/// substitutions, %f and %J. The following is a complete list of valid
|
||||
/// strftime() substitutions:
|
||||
/// * %d day of month: 00
|
||||
/// * %f fractional seconds: SS.SSS
|
||||
/// * %H hour: 00-24
|
||||
/// * %j day of year: 001-366
|
||||
/// * %J Julian day number (fractional)
|
||||
/// * %m month: 01-12
|
||||
/// * %M minute: 00-59
|
||||
/// * %s seconds since 1970-01-01
|
||||
/// * %S seconds: 00-59
|
||||
/// * %w day of week 0-6 with Sunday==0
|
||||
/// * %W week of year: 00-53
|
||||
/// * %Y year: 0000-9999
|
||||
/// * %% %
|
||||
Expression<String?> strftime(String format) => FunctionCallExpression(
|
||||
'STRFTIME',
|
||||
[Constant<String>(format), this, const DateTimeModifier._unixEpoch()]);
|
||||
|
||||
/// Apply a modifier that alters the date and/or time.
|
||||
///
|
||||
/// See the factories on [DateTimeModifier] for a list of modifiers that can
|
||||
/// be used with this method.
|
||||
Expression<DateTime?> modify(DateTimeModifier modifier) =>
|
||||
FunctionCallExpression(
|
||||
'unixepoch', [this, const DateTimeModifier._unixEpoch(), modifier]);
|
||||
|
||||
/// Applies modifiers that alters the date and/or time.
|
||||
///
|
||||
/// The [modifiers] are applied in sequence from left to right.
|
||||
/// For a list of modifiers and how they behave, see the docs on
|
||||
/// [DateTimeModifier] factories.
|
||||
Expression<DateTime?> modifyAll(Iterable<DateTimeModifier> modifiers) =>
|
||||
FunctionCallExpression('unixepoch',
|
||||
[this, const DateTimeModifier._unixEpoch(), ...modifiers]);
|
||||
|
||||
/// Returns an expression containing the amount of seconds from the unix
|
||||
/// epoch (January 1st, 1970) to `this` datetime expression. The datetime is
|
||||
|
@ -69,13 +107,23 @@ extension DateTimeExpressions on Expression<DateTime?> {
|
|||
// anything when converting
|
||||
Expression<int> get secondsSinceEpoch => dartCast();
|
||||
|
||||
/// Adds [duration] from this date.
|
||||
/// Adds a [duration] to this date.
|
||||
///
|
||||
/// Note that the curation is added as a value in seconds. Thus, adding a
|
||||
/// `Duration(days: 1)` will not necessary yield the same time tomorrow in all
|
||||
/// cases (due to daylight saving time switches).
|
||||
/// To change the value in terms of calendar units, see [modify].
|
||||
Expression<DateTime> operator +(Duration duration) {
|
||||
return _BaseInfixOperator(this, '+', Variable<int>(duration.inSeconds),
|
||||
precedence: Precedence.plusMinus);
|
||||
}
|
||||
|
||||
/// Subtracts [duration] from this date.
|
||||
///
|
||||
/// Note that the curation is subtracted as a value in seconds. Thus,
|
||||
/// subtracting a `Duration(days: 1)` will not necessary yield the same time
|
||||
/// yesterday in all cases (due to daylight saving time switches). To change
|
||||
/// the value in terms of calendar units, see [modify].
|
||||
Expression<DateTime> operator -(Duration duration) {
|
||||
return _BaseInfixOperator(this, '-', Variable<int>(duration.inSeconds),
|
||||
precedence: Precedence.plusMinus);
|
||||
|
@ -107,3 +155,110 @@ class _StrftimeSingleFieldExpression extends Expression<int?> {
|
|||
other.date == date;
|
||||
}
|
||||
}
|
||||
|
||||
/// DateTime modifier constants.
|
||||
///
|
||||
/// These modifiers are used on [DateTimeExpressions.modify] and
|
||||
/// [DateTimeExpressions.modifyAll] to apply transformations on date time
|
||||
/// values.
|
||||
///
|
||||
/// For instance, [DateTimeModifier.days] can be used to add or subtract
|
||||
/// calendar days from a date time value. Note that this is different from
|
||||
/// just subtracting a duration with [DateTimeExpressions.+], which only adds a
|
||||
/// duration as seconds without respecting calendar units.
|
||||
///
|
||||
/// For another explanation of modifiers, see the [sqlite3 docs].
|
||||
///
|
||||
/// [sqlite3 docs]: https://sqlite.org/lang_datefunc.html#modifiers
|
||||
class DateTimeModifier extends Constant<String> {
|
||||
const DateTimeModifier._(super.value);
|
||||
|
||||
/// Adds or subtracts [days] calendar days from the date time value.
|
||||
const DateTimeModifier.days(int days) : this._('$days days');
|
||||
|
||||
/// Adds or subtracts [hours] hours from this date time value.
|
||||
const DateTimeModifier.hours(int hours) : this._('$hours hours');
|
||||
|
||||
/// Adds or subtracts [minutes] minutes from this date time value.
|
||||
const DateTimeModifier.minutes(int minutes) : this._('$minutes minutes');
|
||||
|
||||
/// Adds or subtracts [seconds] seconds from this date time value.
|
||||
///
|
||||
/// Note that drift assumes date time values to be encoded as unix timestamps
|
||||
/// (with second accuracy) in the database. So adding seconds with a
|
||||
/// fractional value may not always be preserved in a chain of computation.
|
||||
const DateTimeModifier.seconds(num seconds) : this._('$seconds seconds');
|
||||
|
||||
/// Adds or subtracts [months] months from this date time value.
|
||||
///
|
||||
/// Note that this works by rendering the original date into the `YYYY-MM-DD`
|
||||
/// format, adding the [months] value to the `MM` field and normalizing the
|
||||
/// result. Thus, for example, the date 2001-03-31 modified by '+1 month'
|
||||
/// nitially yields 2001-04-31, but April only has 30 days so the date is
|
||||
/// normalized to 2001-05-01.
|
||||
const DateTimeModifier.months(int months) : this._('$months months');
|
||||
|
||||
/// Adds or subtracts [years] years from this date time value.
|
||||
///
|
||||
/// Similar to the transformation on [DateTimeModifier.months], it may not
|
||||
/// always be possible to keep the day and month field the same for this
|
||||
/// transformation. For instance, if the original date is February 29 of a
|
||||
/// leapyear and one year is added, the result will be in March 1 of the next
|
||||
/// year as there is no February 29.
|
||||
const DateTimeModifier.years(int years) : this._('$years years');
|
||||
|
||||
/// The "start of day" modifier shifts the date backwards to the beginning of
|
||||
/// the day.
|
||||
const DateTimeModifier.startOfDay() : this._('start of day');
|
||||
|
||||
/// The "start of month" modifier shifts the date backwards to the beginning
|
||||
/// of the month.
|
||||
const DateTimeModifier.startOfMonth() : this._('start of month');
|
||||
|
||||
/// The "start of year" modifier shifts the date backwards to the beginning of
|
||||
/// the year.
|
||||
const DateTimeModifier.startOfYear() : this._('start of year');
|
||||
|
||||
/// The "weekday" modifier shifts the date forward to the next date where the
|
||||
/// weekday is the [weekday] provided here.
|
||||
///
|
||||
/// If the source date is on the desired weekday, no transformation happens.
|
||||
DateTimeModifier.weekday(DateTimeWeekday weekday)
|
||||
: this._('weekday ${weekday.index}');
|
||||
|
||||
const DateTimeModifier._unixEpoch() : this._('unixepoch');
|
||||
|
||||
/// Move a date time that is in UTC to the local time zone.
|
||||
///
|
||||
/// See also: [DateTime.toLocal].
|
||||
const DateTimeModifier.localTime() : this._('localtime');
|
||||
|
||||
/// Move a date time that is in the local time zone back to UTC.
|
||||
///
|
||||
/// See also: [DateTime.toLocal].
|
||||
const DateTimeModifier.utc() : this._('utc');
|
||||
}
|
||||
|
||||
/// Weekday offset to be used with [DateTimeModifier.weekday]
|
||||
enum DateTimeWeekday {
|
||||
/// Sunday (+0 on [DateTimeModifier.weekday])
|
||||
sunday,
|
||||
|
||||
/// Monday (+1 on [DateTimeModifier.weekday])
|
||||
monday,
|
||||
|
||||
/// Tueday (+2 on [DateTimeModifier.weekday])
|
||||
tuesday,
|
||||
|
||||
/// Wednesday (+3 on [DateTimeModifier.weekday])
|
||||
wednesday,
|
||||
|
||||
/// Thursday (+4 on [DateTimeModifier.weekday])
|
||||
thursday,
|
||||
|
||||
/// Friday (+5 on [DateTimeModifier.weekday])
|
||||
friday,
|
||||
|
||||
/// Saturday (+6 on [DateTimeModifier.weekday])
|
||||
saturday
|
||||
}
|
||||
|
|
|
@ -38,18 +38,70 @@ void main() {
|
|||
});
|
||||
|
||||
test('extracting values from datetime', () {
|
||||
final expr = Variable.withDateTime(DateTime.utc(2020, 09, 04, 8, 55));
|
||||
final expr = Variable.withDateTime(DateTime.utc(2020, 09, 03, 23, 55));
|
||||
|
||||
expect(eval(expr.year), completion(2020));
|
||||
expect(eval(expr.month), completion(9));
|
||||
expect(eval(expr.day), completion(4));
|
||||
expect(eval(expr.hour), completion(8));
|
||||
expect(eval(expr.day), completion(3));
|
||||
expect(eval(expr.hour), completion(23));
|
||||
expect(eval(expr.minute), completion(55));
|
||||
expect(eval(expr.second), completion(0));
|
||||
|
||||
expect(eval(expr.date), completion('2020-09-04'));
|
||||
expect(eval(expr.time), completion('08:55:00'));
|
||||
expect(eval(expr.datetime), completion('2020-09-04 08:55:00'));
|
||||
expect(eval(expr.date), completion('2020-09-03'));
|
||||
expect(eval(expr.modify(const DateTimeModifier.days(3)).date),
|
||||
completion('2020-09-06'));
|
||||
expect(eval(expr.time), completion('23:55:00'));
|
||||
expect(eval(expr.datetime), completion('2020-09-03 23:55:00'));
|
||||
expect(eval(expr.julianday), completion(2459096.496527778));
|
||||
expect(eval(expr.unixepoch), completion(1599177300));
|
||||
expect(eval(expr.strftime('%Y-%m-%d %H:%M:%S')),
|
||||
completion('2020-09-03 23:55:00'));
|
||||
});
|
||||
|
||||
test('date time modifiers', () {
|
||||
final expr = Variable.withDateTime(DateTime.utc(2022, 07, 05));
|
||||
|
||||
expect(eval(expr.modify(const DateTimeModifier.days(2))),
|
||||
completion(DateTime.utc(2022, 07, 07).toLocal()));
|
||||
expect(eval(expr.modify(const DateTimeModifier.months(-2))),
|
||||
completion(DateTime.utc(2022, 05, 05).toLocal()));
|
||||
expect(eval(expr.modify(const DateTimeModifier.years(1))),
|
||||
completion(DateTime.utc(2023, 07, 05).toLocal()));
|
||||
|
||||
expect(eval(expr.modify(const DateTimeModifier.hours(12))),
|
||||
completion(DateTime.utc(2022, 07, 05, 12).toLocal()));
|
||||
expect(eval(expr.modify(const DateTimeModifier.minutes(30))),
|
||||
completion(DateTime.utc(2022, 07, 05, 0, 30).toLocal()));
|
||||
expect(eval(expr.modify(const DateTimeModifier.seconds(30))),
|
||||
completion(DateTime.utc(2022, 07, 05, 0, 0, 30).toLocal()));
|
||||
|
||||
expect(eval(expr.modify(const DateTimeModifier.startOfDay())),
|
||||
completion(DateTime.utc(2022, 07, 05).toLocal()));
|
||||
expect(eval(expr.modify(const DateTimeModifier.startOfMonth())),
|
||||
completion(DateTime.utc(2022, 07, 01).toLocal()));
|
||||
expect(eval(expr.modify(const DateTimeModifier.startOfYear())),
|
||||
completion(DateTime.utc(2022, 01, 01).toLocal()));
|
||||
|
||||
// The original expression is a Tuesday
|
||||
expect(eval(expr.modify(DateTimeModifier.weekday(DateTimeWeekday.tuesday))),
|
||||
completion(DateTime.utc(2022, 07, 05).toLocal()));
|
||||
expect(
|
||||
eval(expr.modify(DateTimeModifier.weekday(DateTimeWeekday.saturday))),
|
||||
completion(DateTime.utc(2022, 07, 09).toLocal()),
|
||||
);
|
||||
|
||||
// drift interprets date time values as timestamps, so going to UTC means
|
||||
// subtracting the UTC offset in SQL. Interpreting that timestamp in Dart
|
||||
// will effectively add it back, so we have the same value bit without the
|
||||
// UTC flag in Dart.
|
||||
expect(eval(expr.modify(const DateTimeModifier.utc())),
|
||||
completion(DateTime(2022, 07, 05)));
|
||||
|
||||
// And vice-versa (note that original expr is in UTC, this one isn't)
|
||||
expect(
|
||||
eval(Variable.withDateTime(DateTime(2022, 07, 05))
|
||||
.modify(const DateTimeModifier.localTime())),
|
||||
completion(DateTime.utc(2022, 07, 05).toLocal()));
|
||||
});
|
||||
|
||||
test('rowid', () {
|
||||
|
|
Loading…
Reference in New Issue