Сравнение дат с formatDate в Google Apps Script отстает на 1 день

Написание скрипта Служб Google, который рассматривает задачи через API задач Google (как расширенный сервис) и проверяет срок их выполнения, чтобы определить, не просрочены ли они. Если они есть, то следует перенести их на текущий день.

Завершенный проект

Я использовал этот код, чтобы создать этот проект для поддержания задач Google в актуальном состоянии:

Постоянно обновляйте задачи Google

вопрос

При сравнении текущего дня с датой выполнения задачи, дата выполнения задачи всегда на один день позже.

Исследование

Посмотрев на это, я обнаружил, что это может быть связано с переходом на летнее время, и только один час отдыха может означать разницу в два дня. Я нашел этот вопрос, который имел похожую проблему, их решение было использовать Session.getScriptTimeZone() правильно установить часовой пояс.

Однако даже когда я установил часовой пояс, используя Session.getScriptTimeZone() у него все еще есть срок выполнения задачи один день позади.

Код

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.');
  }
}

бревна

[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

=== [ОБНОВЛЕНИЕ] ===

Я заметил, что если я войду в оригинал task.due похоже, у него правильная дата. Так что что-то в форматировании меняет дату.

Новый код

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.');
  }
}

Новые журналы

[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

=== [Обновление 2 ] ===

Поменял оба "EST" в формате Date to Session.getScriptTimeZone()

Удалены .valueOf() для сравнения.

Все еще приводит к той же проблеме

Новый код

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.');
  }
}

Новые журналы

[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

2 ответа

Решение

Согласно документации Google Tasks API, task#due свойство является значением даты-времени RFC 3339, например "2018-04-11T0:45:26.000Z", Из этой строки вы можете построить Javascript Date изначально, т.е.

var taskDueDate = new Date(task.due);

Обратите внимание, что это эквивалентно вызову Date.parse() и, следовательно, может быть причиной неудачного сравнения дат, учитывая, что Google Apps Script - это Javascript 1.6 (т.е. не ES5+). Возможно, лучшим способом было бы использовать собственный синтаксический анализатор, учитывая, что вы знаете строго конкретный формат, из которого вы получите task.due, Один такой парсер подробно описан в этом ответе.

Как упоминает Serge insas, вы должны выполнить это сравнение, используя native Date объекты, а не 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;
}

Для информации, это небольшой код, который я использую для преобразования RFC339 объекта времени в дату. Мне легче читать и понимать.

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));
}
Другие вопросы по тегам