'Dart/Flutter DateTime difference inDays error for March 31 April 1

I am trying to get the difference in Days between two dates picked from a DatePicker. This works fine except for ONE single date : March 31.

The difference in Days between two DateTimes is wrong by 1 day when one of the dates is March 31. I know this is due to Light Saving and March is 30.9… days long and not 31, hence I am guessing, the error. But does anyone know how to fix this other than manually checking if a date is equal to March 31 and adding one day to the result ?

Two very simple examples that can be run in the Dart Pad :

DateTime aprilFirst = DateTime(2019, 3, 30);
DateTime marchThirtyFirst = DateTime(2019, 3, 31);

print(aprilFirst.difference(marchThirtyFirst).inDays); => -1

DateTime marchThirty = DateTime(2019, 4, 1);
DateTime marchThirtyFirst = DateTime(2019, 3, 31);

print(marchThirty.difference(marchThirtyFirst).inDays); => 0

UPDATE:

DateTime aprilFirst = DateTime(2019, 4, 1);
print(aprilFirst.add(Duration(days: -1))); => 2019-03-30 23:00:00.000

This should print 2019-03-31 23:00:00.000 !

I tried Günter Zöchbauer's solution of making the DateTimes UTC but the results are the exact same:

DateTime aprilFirst = DateTime(2019, 4, 1).toUtc();
DateTime marchThirty = DateTime(2019, 3, 30).toUtc();
DateTime marchThirtyFirst = DateTime(2019, 3, 31).toUtc();

print(aprilFirst.difference(marchThirtyFirst).inHours); => 23
print(aprilFirst.difference(marchThirtyFirst).inDays); => 0
print(marchThirty.difference(marchThirtyFirst).inHours); => -24
print(aprilFirst.add(Duration(days: -1))); => 019-03-30 22:00:00.000Z


Solution 1:[1]

@Günter Zöchbauer put me on the right path. DateTime(...).toUTC() will fail for difference calculations. However, using the DateTime.utc(...) constructor does the trick !

DateTime aprilFirst = DateTime.utc(2019, 4, 1);
DateTime marchThirty = DateTime.utc(2019, 3, 30);
DateTime marchThirtyFirst = DateTime.utc(2019, 3, 31);

print(aprilFirst.difference(marchThirtyFirst).inHours); => 24
print(aprilFirst.difference(marchThirtyFirst).inDays); => 1
print(marchThirty.difference(marchThirtyFirst).inHours); => -24
print(aprilFirst.add(Duration(days: -1))); => 2019-03-31 00:00:00.000Z

Solution 2:[2]

Don't do Date comparison or operations with local dates. Convert it to UTC first. Otherwise daylight savings and other local DateTime related exceptions will cause all kinds of surprising effects.

DateTime aprilFirst = DateTime(2019, 3, 30).toUtc();
DateTime marchThirtyFirst = DateTime(2019, 3, 31).toUtc();

print(aprilFirst.difference(marchThirtyFirst).inDays); => -1

If the result is a DateTime you can convert it back using xxx.toLocal()

There is also a constructor that allows to create an UTC DateTime instead of creating a local DateTime and then converting to UTC.

Solution 3:[3]

Try this package, Jiffy uses the momentJs concept of date-time difference

You can see dicussion here https://github.com/moment/moment/pull/571

DateTime aprilFirst = DateTime(2019, 3, 30);
DateTime marchThirtyFirst = DateTime(2019, 3, 31);

Jiffy(aprilFirst).diff(marchThirtyFirst, Units.DAY); // -1
// or
Jiffy([2019, 3, 30]).diff([2019, 3, 31], Units.DAY); // -1

DateTime marchThirty = DateTime(2019, 4, 1);
DateTime marchThirtyFirst = DateTime(2019, 3, 31);

Jiffy(marchThirty).diff(marchThirtyFirst, Units.DAY); // 1
Jiffy(marchThirty).diff(marchThirtyFirst, Units.HOUR); // 24
Jiffy(marchThirty).diff(marchThirtyFirst, Units.MONTH); // 0

Solution 4:[4]

2021 Based on answer

extension DateCalcs on DateTime {
  DateTime get dateOnlyUTC => DateTime.utc(this.year,this.month,this.day);
}

Solution 5:[5]

You've already figured out that you get an unexpected result due to your locale observing Daylight Saving Time. Duration internally stores a number of microseconds; Duration.days is measured in terms of 24-hour increments and not in terms of calendar days. From the DateTime documentation:

The difference between two dates in different time zones is just the number of nanoseconds between the two points in time. It doesn't take calendar days into account. That means that the difference between two midnights in local time may be less than 24 hours times the number of days between them, if there is a daylight saving change in between. If the difference above is calculated using Australian local time, the difference is 7415 days and 23 hours, which is only 7415 whole days as reported by inDays.

See https://stackoverflow.com/a/71198806/ for a more general version of Benjamin's answer that computes the difference in days between two dates, ignoring the time (and therefore also ignoring Daylight Saving adjustments and time zones).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Günter Zöchbauer
Solution 3
Solution 4 Roddy R
Solution 5