relative method

DateTime? relative(
  1. String? modifier
)

Constructs a new DateTime instance based on modifier.

The modifier string must not be null. Returns null if the modifier string cannot be parsed.

This function parses the modifier string using this format:

[+|-|blank]<integer><unit>@[+|-|blank]<unit>

When the sign character is missing, '+' is assumed.

For the instance date and time, use now.

Examples:

  • '-1d' => subtract one day
  • '+1w' => add one week
  • '-6m' => subtract six months
  • '2y' => add two years
  • 'now' => current time in this instance

Code examples:

  • DateTime.now().relative('-1d') => yesterday
  • DateTime.now().relative('@w') => Sunday
  • DateTime.now().relative('-1y@y') => Last New Year's Eve
  • DateTime.now().relative('@-y') => Last New Year's Day
  • DateTime(2020, 4, 2, 12).relative('-1d') => noon on April fool's day
  • DateTime(2001, 9, 16).relative("-5d") => 9/11

The @ character indicates that the instance should adjust (snap) to the start (-) or end (+) of the unit.

Examples:

  • '@-d' => start of the day
  • '@+w' => end of the week
  • '@m' => end of the month
  • '@-y => start of the year

Code examples:

  • DateTime.now().relative('@-y') => start of this year
  • DateTime.now().relative('+y') => end of this year
  • DateTime.now().relative('-1y@-y') => start of last year
  • DateTime.now().relative('-1y@+y') => end of last year

BNF:

  • modifier ::= time_modifier snap_to
  • time_modifier ::=
  • unit ::= 'd' | 'w' | 'm' | 'y'
  • time_integer ::= digit{*}
  • sign ::=
  • sign_opt ::=
  • snap_to ::=
  • snap ::= '@' unit snap_modifier
  • snap_modifier ::=

Snap moves to start or end of time unit after applying time modifier. You specify the start of a time unit with '-' character, and end of the time unit with the '+' character, and no character defaults to end. Snap examples:

  • Start of current month: @-m

  • End of current month: @+m or @m

  • Start of previous month: -1m@-m

  • End of previous month: -1m@+m or -1m@m

  • Start of current year: @-y

  • End of current year: @+y or @y

  • Start of previous year: -1y@-y

  • End of previous year: -1y@+y or -1y@y

Implementation

/// Snap examples:
/// * Start of current month: @-m
/// * End of current month: @+m or @m
/// * Start of previous month: -1m@-m
/// * End of previous month: -1m@+m or -1m@m
///
/// * Start of current year: @-y
/// * End of current year: @+y or @y
/// * Start of previous year: -1y@-y
/// * End of previous year: -1y@+y or -1y@y
DateTime? relative(String? modifier) {
  final parser = DateTimeRelativeParser();
  final parsed = parser.relativeParse(modifier);
  if (parsed.error) {
    return null;
  }
  if (parsed.now) {
    return this;
  }

  final jif =
      Jiffy.unixFromMillisecondsSinceEpoch(this!.millisecondsSinceEpoch);
  if (parsed.timeUnitMatch == 'd') {
    parsed.neg!
        ? jif.subtract(days: parsed.timeInt!)
        : jif.add(days: parsed.timeInt!);
  } else if (parsed.timeUnitMatch == 'w') {
    parsed.neg!
        ? jif.subtract(weeks: parsed.timeInt!)
        : jif.add(weeks: parsed.timeInt!);
  } else if (parsed.timeUnitMatch == 'm') {
    parsed.neg!
        ? jif.subtract(months: parsed.timeInt!)
        : jif.add(months: parsed.timeInt!);
  } else if (parsed.timeUnitMatch == 'y') {
    parsed.neg!
        ? jif.subtract(years: parsed.timeInt!)
        : jif.add(years: parsed.timeInt!);
  }
  if (parsed.snapTimeUnitMatch != null) {
    if (parsed.snapTimeUnitMatch == 'd') {
      parsed.snapNeg ? jif.startOf(Units.DAY) : jif.endOf(Units.DAY);
    } else if (parsed.snapTimeUnitMatch == 'w') {
      parsed.snapNeg ? jif.startOf(Units.WEEK) : jif.endOf(Units.WEEK);
    } else if (parsed.snapTimeUnitMatch == 'm') {
      parsed.snapNeg ? jif.startOf(Units.MONTH) : jif.endOf(Units.MONTH);
    } else if (parsed.snapTimeUnitMatch == 'y') {
      parsed.snapNeg ? jif.startOf(Units.YEAR) : jif.endOf(Units.YEAR);
    }
  }
  return jif.dateTime;
}