0

Writing a Google Apps Script that looks at tasks via the Google Tasks API (as an Advanced Service) and checks their due date to see if they are overdue. If they are, it should then move them to the current day.

Completed Project

I used this code to create this project for keeping Google Tasks up to date:

Keep Google Tasks Updated

Issue

When comparing the current day with the date the task is due, the date the task is due is always one day behind.

Research

Looking into it I found that it could have to do with Daylight Savings Time and just one hour off could mean the difference in two days. I found this question that had a similar issue, their solution was to use Session.getScriptTimeZone() to set the timezone correctly.

However even when I set the timezone using Session.getScriptTimeZone() it still has the task's due date one day behind.

The Code

function checkOverdue(t) {
  var today = Utilities.formatDate(new Date(), "EST", "MM/dd/yyyy");
  //var today = new Date();
  var tasks = t;
  if (tasks.items) {
    for (var i = 0; i < tasks.items.length; i++) {
      var task = tasks.items[i];
      if (task.due) {
        var taskDue = Utilities.formatDate(new Date(task.due), "EST", "MM/dd/yyyy");
        Logger.log('"%s", Comparing "%s" to "%s"',
                 task.title, taskDue, today);
        if (taskDue.valueOf() < today.valueOf()) {
          Logger.log('Task with title "%s" is past due', task.title);
        }
      }
    }
  } else {
    Logger.log('No tasks found.');
  }
}

Logs

[18-04-10 13:12:33:927 EDT] "Create Bio presentation", Comparing "04/09/2018" to "04/10/2018"
[18-04-10 13:12:33:928 EDT] Task with title "Create Bio presentation" is past due
[18-04-10 13:12:33:929 EDT] "Bio Presentation", Comparing "04/10/2018" to "04/10/2018"
[18-04-10 13:12:33:930 EDT] "Matrix HW", Comparing "04/09/2018" to "04/10/2018"
[18-04-10 13:12:33:930 EDT] Task with title "Matrix HW" is past due

=== [ UPDATE ] ===

I noticed that if I log the original task.due it appears to have the correct date. So something about the formatting is changing the date.

New Code

function checkOverdue(t) {
  var today = Utilities.formatDate(new Date(), "EST", "MM/dd/yyyy");
  //var today = new Date();
  var tasks = t;
  if (tasks.items) {
    for (var i = 0; i < tasks.items.length; i++) {
      var task = tasks.items[i];
      if (task.due) {
        var taskDue = Utilities.formatDate(new Date(task.due), "EST", "MM/dd/yyyy");
        Logger.log('"%s", Comparing TaskDue: "%s" to today: "%s", task.due "%s"', task.title, taskDue, today, task.due);
        if (taskDue.valueOf() < today.valueOf()) {
          Logger.log('Task with title "%s" is past due', task.title);
        }
      }
    }
  } else {
    Logger.log('No tasks found.');
  }
}

New Logs

[18-04-10 14:15:17:628 EDT] "Create Bio presentation", Comparing TaskDue: "04/09/2018" to today: "04/10/2018", task.due "2018-04-10T00:00:00.000Z"
[18-04-10 14:15:17:629 EDT] Task with title "Create Bio presentation" is past due
[18-04-10 14:15:17:630 EDT] "Bio Presentation", Comparing TaskDue: "04/10/2018" to today: "04/10/2018", task.due "2018-04-11T00:00:00.000Z"
[18-04-10 14:15:17:631 EDT] "Matrix HW", Comparing TaskDue: "04/09/2018" to today: "04/10/2018", task.due "2018-04-10T00:00:00.000Z"
[18-04-10 14:15:17:631 EDT] Task with title "Matrix HW" is past due

=== [ Update 2 ] ===

Changed both "EST" in formatDate to Session.getScriptTimeZone()

Removed .valueOf() for comparison.

Still results in the same issue

New Code

function checkOverdue(t) {
  var today = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "MM/dd/yyyy");
  //var today = new Date();
  var tasks = t;
  if (tasks.items) {
    for (var i = 0; i < tasks.items.length; i++) {
      var task = tasks.items[i];
      if (task.due) {
        var taskDue = Utilities.formatDate(new Date(task.due), Session.getScriptTimeZone(), "MM/dd/yyyy");
        Logger.log('"%s", Comparing TaskDue: "%s" to today: "%s", task.due "%s"', task.title, taskDue, today, task.due);
        if (taskDue < today) {
          Logger.log('Task with title "%s" is past due', task.title);
        }
      }
    }
  } else {
    Logger.log('No tasks found.');
  }
}

New Logs

[18-04-10 15:21:01:988 EDT] "Create Bio presentation", Comparing TaskDue: "04/09/2018" to today: "04/10/2018", task.due "2018-04-10T00:00:00.000Z"
[18-04-10 15:21:01:988 EDT] Task with title "Create Bio presentation" is past due
[18-04-10 15:21:01:990 EDT] "Bio Presentation", Comparing TaskDue: "04/10/2018" to today: "04/10/2018", task.due "2018-04-11T00:00:00.000Z"
[18-04-10 15:21:01:991 EDT] "Matrix HW", Comparing TaskDue: "04/09/2018" to today: "04/10/2018", task.due "2018-04-10T00:00:00.000Z"
[18-04-10 15:21:01:991 EDT] Task with title "Matrix HW" is past due
CTOverton
  • 616
  • 2
  • 9
  • 20
  • 1
    Why are you still using hard coded "EST" timezone in some parts of your code ? You should replace this everywhere to get consistent results. – Serge insas Apr 10 '18 at 18:50
  • 1
    Also, you shouldn't convert dates to strings, the date methods don't work on strings obviously so when you're using valueOf() in the comparison it doesn't make sense. Leave dates as dates and things should work. – Serge insas Apr 10 '18 at 18:53
  • Thank you so much @Sergeinsas, I revised the code but I'm still getting the same results. I showed what I did in the question above in Update 2 – CTOverton Apr 10 '18 at 19:25
  • @Sergeinsas with more messing around I noticed task.due is a string, not a date. So the comparison doesn't seem to work if it's not a string. And that's an issue because when converting it to a string it gets the wrong day – CTOverton Apr 10 '18 at 19:55
  • Convert task.due to a date object, that's the best way to go. If you need help on how to achieve that feel free to let us now – Serge insas Apr 10 '18 at 22:38
  • @Sergeinsas okay! I would imagine that would be along the lines of var newTaskDate = new Date(task.due) However if this is the case when I do that I still get it to be one day off. I can fix this easily by adding one day. So after that all I really need is how to set the new task date https://stackoverflow.com/questions/49763474/how-to-set-google-tasks-due-date – CTOverton Apr 10 '18 at 22:44
  • This would probably be a source of trouble in a few months when daylight savings switch again...Are you certain that the calendar and the script have the same timezone settings ? – Serge insas Apr 10 '18 at 22:52
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/168685/discussion-between-ctoverton-and-serge-insas). – CTOverton Apr 10 '18 at 22:53

2 Answers2

1

Per Google Tasks API documentation, the task#due property is an RFC 3339 datetime value, e.g. "2018-04-11T0:45:26.000Z". From this string, you can construct a Javascript Date natively, i.e.

var taskDueDate = new Date(task.due);

Note that this is equivalent to calling Date.parse() and thus could be the reason your date comparisons fail, given that Google Apps Script is Javascript 1.6 (i.e. not ES5+). A possibly better method would be to use a custom parser, given that you know the strict specific format that you will get from task.due. One such parser is detailed in this answer.

As @Serge mentions, you should perform this comparison using native Date objects, and not Strings:

function isOverdue(task) {
  if(!task.due)
    return false;

  var now = new Date();
  var endOfToday = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1)); // Also known as start of tomorrow ;)
  var taskDueDate = myFunctionToConvertRFC3339StringToJSDate(task.due);
  return taskDueDate < endOfToday;
}
Community
  • 1
  • 1
tehhowch
  • 9,645
  • 4
  • 24
  • 42
1

for info, this is a small code I use to convert RFC339 time to date object. I find it easier to read and understand.

function parseDate_RFC3339(string) {
  var refStr = new Date().toString();
  Logger.log('refStr = '+refStr);
  var tzOffset = Number(refStr.substr(refStr.indexOf('GMT')+4,2));
  Logger.log('TZ offset = '+tzOffset);
  var parts = string.split('T');
  parts[0] = parts[0].replace(/-/g, '/');
  var t = parts[1].split(':');
  return new Date(new Date(parts[0]).setHours(+t[0]+tzOffset,+t[1],0));
}
Serge insas
  • 45,904
  • 7
  • 105
  • 131