I have an app where users track their streaks. Streaks are breaking today because of the way daylight savings time is treated. Dates are stored in the Firebase Database as a string in "MM/dd/yyyy" format. So for a user who used the app on Sunday, November 3rd, 2019 the string value is "11/03/2019" that converts to a date value that prints

2019-11-03 00:00:00.000

Here is how I am chopping off hours/minutes from the current date so that I can compare it to other dates:

final dateFormat = intl.DateFormat('MM/dd/yyyy');
final todayString = dateFormat.format(DateTime.now());
final today = dateFormat.parse(todayString);
print('Today is ' + today.toString());

When I print the value of today I get:

2019-11-04 00:00:00.000

When I attempt to get the DateTime value of yesterday like this:

 today.subtract(Duration(days: 1))

The value prints out like so:

2019-11-03 01:00:00.000

Notice the hour value now has a one in it.

To determine whether to break or continue the streaks. I am comparing the user's last date of use with yesterday using the DateTime.compareTo() function.

final activeStreak = this.lastTraining.compareTo(today.subtract(Duration(days: 1)));

For a user who used the app on Sunday, November 3rd, 2019, the value of should come back as 0, because yesterday should equal yesterday. But because of daylight savings time, now yesterday is being treated as before yesterday, so it is returning a value of -1.

How can I prevent this bug in following years?


Solution 1: Rob Castillo

May I suggest creating DateTime objects using the DateTime.utc() constructor, it'll prevent Daylight Saving issues. Compare these two examples:

void main() {
  //Example 1: Without UTC
  DateTime yesterday = DateTime(2019, 11, 3);
  DateTime today = DateTime(2019, 11, 4);
  DateTime now = DateTime.now();
  print(yesterday);                             // 2019-11-03 00:00:00.000
  print(today.subtract(Duration(days: 1)));     // 2019-11-03 01:00:00.000
  print(yesterday.compareTo(today.subtract(Duration(days: 1)))); // -1
  print(today.compareTo(now));

  print("");

  //Example 2: With UTC constructor
  DateTime yesterdayUtc = DateTime.utc(2019, 11, 3);
  DateTime todayUtc = DateTime.utc(2019, 11, 4);
  DateTime nowUtc = DateTime.now().toUtc();
  print(yesterdayUtc);                           // 2019-11-03 00:00:00.000Z
  print(todayUtc.subtract(Duration(days: 1)));   // 2019-11-03 00:00:00.000Z
  print(yesterdayUtc.compareTo(todayUtc.subtract(Duration(days: 1)))); // 0
  print(today.compareTo(nowUtc));
}

DartPad link: https://dartpad.dartlang.org/c4244a97890798373ee053f04032ccad

When dealing with dates or historic events prefer to use UTC DateTimes, since they are unaffected by daylight-saving changes and are unaffected by the local timezone.

Source: https://api.dartlang.org/stable/2.6.0/dart-core/DateTime/DateTime.utc.html