4

I'm running this on flutter, but I guess this could be a more general issue.

I am saving a DateTime in the preferences. I want to be able to then tell if DateTime.now() is on at least a day after the last saved DateTime, i.e.

(pseudocode)
lastDailyCheck = 2020.04.10
now = 2020.04.11

=> now is a day after the lastDailyCheck.

This should already work if it is 00:01 on the new day, even if the lastDailyCheck was on 23:58 the day before, meaning the difference can be as low as minutes between the 2 DateTimes.

Turns out this is really complicated!

Here's what doesn't work:

DateTime.Now().isAfter(lastDailyCheck)

This only checks if now is after the last one, it also return true after a second, and on the same day.

DateTime.Now().isAfter(lastDailyCheck) && lastDailyCheck.day != DateTime.Now().day

I thought this was clever. If the day is different and it is after the last then it does work in recognizing it is a day later - but then I realized it would bug out when both days are say on the 15th of the month - then lastDailyCheck.day would equal DateTime.Now().day.

What do you think would be possible here?

7 Answers 7

5

I don't know flutter, but my approach would be to not store the last check, but store the date at which the next check should occur. So when you perform a check you calculate the next midnight and store that. Now you can use isAfter.

In javascript this would look something like this:

const now = new Date();

//this also handles overflow into the next month
const nextCheck = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)

//store nextCheck somewhere

//in js there is no isAfter, you just use >
if(new Date() > nextCheck) {
   //do the thing
}

of course you could also calculate nextCheck every time you want to compare it, but I dislike performing the same calculation over and over if I can avoid it.

A thing to mention here is timezones, depending on your date library and if your system and user timezones align, you may need to shift the date.

2
  • That sounds like a much more clever way to do it, I'll try that! :D Commented Apr 18, 2020 at 17:33
  • I know this has nothing to do with the original question, but this answer actually SAVED my code since I was trying to add a fixed amount of days in my calculations and I had problems with the DST! Using something like DateTime(2021, 11, 01-7) actually includes the DST calculations for us. Wow! Thank you!
    – venir
    Commented Nov 8, 2021 at 9:12
4

I cannot write a complete code for now but this is what it would look like:

(pseudocode)

expirationDay = lastDailyCheck.add(oneDayDuration);
isOneDayAfter = DateTime.now().isAfter(expirationDay);

You give an expiration date and compare the DateTime to that. You have to use isAfter for reliability, instead of .day check.

1
  • Cool Approach! :) Not sure it would fully work when the actual difference between the 2 days would be less than 24h, e.g. lastDailyCheck at 23:00, and the current day at 12:00 Commented Apr 17, 2020 at 9:12
2

I would compute the difference between midnight of the day of the last timestamp and midnight of the current timestamp. That is, consider only the date portion of a DateTime and ignore the time.

DateTime date(DateTime dateTime) =>
    DateTime(dateTime.year, dateTime.month, dateTime.day);

// Intentionally check for a positive difference in hours instead of days
// so we don't need to worry about 23-hour days from DST.  Any non-zero
// number of hours here means a difference of at least a "day".
if (date(DateTime.now()).difference(date(lastDailyCheck)).inHours > 0) {
  // "One day" after.
}

If you're using UTC timestamps and don't care about when midnight is in whatever the local time is, the comparison could more intuitively use .inDays >= 1.

2

Figured out another potential solution!

In addition to checking if the day is different (which by itself won't work) you can also check the month and year. Only 1 of those needs to differ for it be true :)

if (now.isAfter(lastDailyCheck)) {
  if (now.day != lastDailyCheck.day ||
      now.month != lastDailyCheck.month ||
      now.year != lastDailyCheck.year) {
    return true;
  }
}
2

this is the way I prefer to do some logics based on the comparison between two different times:

var now = DateTime.now();
var myDate = new DateTime.utc(2022, 1, 1);
if(myDate.compareTo(now)>0) //positive value means myDate is greater than DateTime.now()
{
  // here is your logic based on the comparison between two times
} else {
 //your logic if DateTime.now() pass myDate
}
1

You can use the difference method to get the difference between 2 dates and check whether those differs in hours with at-least 24 hours. So your if condition becomes:

if (now.isAfter(lastDailyCheck) &&
    (lastDailyCheck.day != now.day ||
    now.difference(lastDailyCheck).inHours > 24)) {
    print('After');
}
1
  • Neat, that should bypass the potential lastDailyCheck.day != now.day issue! :D Commented Apr 17, 2020 at 9:02
1

Here's an attempt to a succint answer. Simply export this extension and use it. With it you can say if a date is at least one day after the current day.

extension DateExt on DateTime {
  bool isAtLeastOneDayAfterToday() {
    final now = DateTime.now();

    return (isAfter(now) &&
        (day != now.day || month != now.month || year != now.year));
  }
}

Use it like so:

final isAfter = myDay.isAtLeastOneDayAfterToday(); //will be true or false

Not the answer you're looking for? Browse other questions tagged or ask your own question.