Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 278
Benjamin Avatar asked Apr 02 '19 10:04

Benjamin


4 Answers

@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
like image 77
Benjamin Avatar answered Oct 09 '22 10:10

Benjamin


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.

like image 32
Günter Zöchbauer Avatar answered Oct 09 '22 11:10

Günter Zöchbauer


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
like image 1
Jama Mohamed Avatar answered Oct 09 '22 10:10

Jama Mohamed


2021 Based on answer

extension DateCalcs on DateTime {
  DateTime get dateOnlyUTC => DateTime.utc(this.year,this.month,this.day);
}
like image 1
Roddy R Avatar answered Oct 09 '22 09:10

Roddy R