Работа с датами является фундаментальной задачей во многих скриптах автоматизации, от планирования задач до анализа данных временных рядов. Google Apps Script (GAS) предоставляет стандартные возможности JavaScript для манипулирования датами, дополненные специфичными для Google Workspace утилитами.
Тип данных Date в JavaScript и Google Apps Script
Основным инструментом для работы с датами в GAS является встроенный объект Date JavaScript. Он представляет собой единый момент времени, хранящийся как количество миллисекунд, прошедших с начала эпохи Unix (1 января 1970 года UTC).
/**
* Демонстрация создания объекта Date.
*/
function demonstrateDateObject(): void {
const specificDate: Date = new Date('2023-10-26T10:00:00Z'); // Дата по UTC
const fromTimestamp: Date = new Date(1678886400000); // Дата из timestamp
Logger.log('Specific Date: %s', specificDate.toString());
Logger.log('Date from Timestamp: %s', fromTimestamp.toString());
}Понимание того, что Date внутренне оперирует UTC, критически важно при работе с часовыми поясами.
Получение текущей даты и времени
Получить текущую дату и время в GAS просто — достаточно создать новый экземпляр объекта Date без аргументов.
/**
* Получает и логирует текущую дату и время.
*/
function logCurrentDateTime(): void {
const now: Date = new Date();
Logger.log('Текущая дата и время: %s', now);
// В логах Apps Script дата обычно отображается в часовом поясе сервера
}Основные способы добавления дней к дате
Существует несколько подходов к добавлению определенного количества дней к существующей дате в JavaScript и, соответственно, в GAS.
Использование метода setDate() для изменения даты
Метод setDate() объекта Date изменяет день месяца для указанной даты в соответствии с локальным временем. Он автоматически обрабатывает переходы через конец месяца и года.
/**
* Добавляет указанное количество дней к дате с использованием setDate().
*
* @param {Date} startDate Исходная дата.
* @param {number} daysToAdd Количество дней для добавления (может быть отрицательным).
* @return {Date} Новая дата.
*/
function addDaysUsingSetDate(startDate: Date, daysToAdd: number): Date {
const resultDate = new Date(startDate);
resultDate.setDate(resultDate.getDate() + daysToAdd);
return resultDate;
}
/**
* Пример использования addDaysUsingSetDate.
*/
function testAddDaysSetDate(): void {
const today: Date = new Date();
const futureDate: Date = addDaysUsingSetDate(today, 10);
Logger.log('Сегодня: %s', today);
Logger.log('Через 10 дней: %s', futureDate);
const lastDayOfYear: Date = new Date(today.getFullYear(), 11, 31); // 31 декабря
const nextYearDate: Date = addDaysUsingSetDate(lastDayOfYear, 1);
Logger.log('Последний день года: %s', lastDayOfYear);
Logger.log('Плюс 1 день: %s', nextYearDate); // Корректно перейдет на следующий год
}Этот метод интуитивно понятен и хорошо подходит для большинства простых задач.
Математические операции с датами (getTime() и setTime())
Альтернативный подход — работать с миллисекундами. Метод getTime() возвращает количество миллисекунд с начала эпохи Unix, а setTime() устанавливает дату на основе этого значения. Это позволяет выполнять арифметические операции.
/**
* Добавляет указанное количество дней к дате через манипуляции с миллисекундами.
*
* @param {Date} startDate Исходная дата.
* @param {number} daysToAdd Количество дней для добавления.
* @return {Date} Новая дата.
*/
function addDaysUsingMilliseconds(startDate: Date, daysToAdd: number): Date {
const millisecondsInDay: number = 24 * 60 * 60 * 1000;
const startTime: number = startDate.getTime();
const newTime: number = startTime + (daysToAdd * millisecondsInDay);
const resultDate = new Date(newTime);
return resultDate;
}
/**
* Пример использования addDaysUsingMilliseconds.
*/
function testAddDaysMilliseconds(): void {
const today: Date = new Date();
const futureDate: Date = addDaysUsingMilliseconds(today, 7);
Logger.log('Сегодня: %s', today);
Logger.log('Через 7 дней (мс): %s', futureDate);
}Этот метод менее подвержен некоторым проблемам с переходом на летнее/зимнее время, если точность до дня достаточна, так как оперирует фиксированными интервалами в 24 часа. Однако при работе с календарями и событиями setDate() часто предпочтительнее из-за учета локального времени.
Реализация добавления дней с учетом часового пояса
Часовые пояса — одна из основных сложностей при работе с датами, особенно в глобальных приложениях.
Проблемы с часовыми поясами при работе с датами
Методы вроде getDate() и setDate() работают с локальным временем скрипта (часто совпадает с часовым поясом аккаунта Google или настройками проекта). Прямое добавление дней через setDate() может привести к неожиданным результатам при пересечении границ перехода на летнее/зимнее время (DST), так как длительность суток в локальном времени может быть не ровно 24 часа.
Добавление через миллисекунды (getTime/setTime) основано на UTC и всегда добавляет ровно N * 24 часа. Это может быть как преимуществом (стабильность), так и недостатком, если требуется точное соответствие локальным календарным дням.
Использование Utilities.formatDate() для корректного отображения
Независимо от способа добавления дней, для корректного отображения даты пользователю в определенном часовом поясе следует использовать сервис Utilities.
/**
* Добавляет дни и форматирует результат для указанного часового пояса.
*
* @param {Date} startDate Исходная дата.
* @param {number} daysToAdd Количество дней.
* @param {string} timeZone Целевой часовой пояс (например, 'Europe/Moscow', 'America/New_York').
* @param {string} format Формат даты (например, 'yyyy-MM-dd HH:mm:ss').
* @return {string} Отформатированная строка с датой.
*/
function addDaysAndFormat(startDate: Date, daysToAdd: number, timeZone: string, format: string): string {
// Используем setDate для добавления дней, учитывая локальные календарные дни
const resultDate: Date = addDaysUsingSetDate(startDate, daysToAdd);
// Форматируем результат в нужном часовом поясе
const formattedDate: string = Utilities.formatDate(resultDate, timeZone, format);
return formattedDate;
}
/**
* Пример использования addDaysAndFormat.
*/
function testFormatDate(): void {
const eventStartDate: Date = new Date(); // Текущее время
const campaignDurationDays: number = 30;
const timeZone: string = Session.getScriptTimeZone(); // Или указать конкретный
const dateFormat: string = 'dd.MM.yyyy';
const campaignEndDateFormatted: string = addDaysAndFormat(eventStartDate, campaignDurationDays, timeZone, dateFormat);
Logger.log('Дата начала кампании (%s): %s', timeZone, Utilities.formatDate(eventStartDate, timeZone, dateFormat));
Logger.log('Дата окончания кампании (%s): %s', timeZone, campaignEndDateFormatted);
}Utilities.formatDate() позволяет представить внутреннее значение Date (UTC) в читаемом виде для любого часового пояса.
Примеры кода добавления дней к дате в Google Apps Script
Рассмотрим практические примеры.
Простой скрипт для добавления N дней к текущей дате
/**
* Логирует дату, которая наступит через N дней от текущей.
*
* @param {number} numberOfDays Количество дней для добавления.
*/
function logFutureDate(numberOfDays: number): void {
if (typeof numberOfDays !== 'number' || !Number.isInteger(numberOfDays)) {
Logger.log('Ошибка: Количество дней должно быть целым числом.');
return;
}
const today: Date = new Date();
const futureDate: Date = addDaysUsingSetDate(today, numberOfDays);
const timeZone: string = Session.getScriptTimeZone();
const format: string = 'yyyy-MM-dd';
Logger.log('Текущая дата: %s', Utilities.formatDate(today, timeZone, format));
Logger.log('Дата через %s дней: %s', numberOfDays, Utilities.formatDate(futureDate, timeZone, format));
}
// Вызов примера
// logFutureDate(15);Функция для добавления дней к дате в Google Sheets
Можно создать пользовательскую функцию для использования прямо в ячейках Google Таблиц.
/**
* Добавляет указанное количество дней к дате. Работает как пользовательская функция в Google Sheets.
*
* @param {Date | string | number} dateInput Исходная дата (может быть датой, строкой или числом из ячейки).
* @param {number} daysToAdd Количество дней для добавления.
* @return {Date} Новая дата.
* @customfunction
*/
function ADD_DAYS(dateInput: any, daysToAdd: number): Date | string {
if (daysToAdd === null || daysToAdd === undefined || typeof daysToAdd !== 'number') {
return '#ERROR: Количество дней (второй аргумент) должно быть числом.';
}
let startDate: Date;
try {
// Пытаемся преобразовать входное значение в дату
startDate = new Date(dateInput);
// Проверяем, является ли результат валидной датой
if (isNaN(startDate.getTime())) {
throw new Error('Некорректный формат даты.');
}
} catch (e) {
return `#ERROR: Не удалось распознать дату (первый аргумент). ${e.message}`;
}
const resultDate = new Date(startDate);
resultDate.setDate(resultDate.getDate() + daysToAdd);
return resultDate; // Sheets автоматически отформатирует как дату
}После добавления этого кода в редактор скриптов Таблицы, в ячейке можно будет написать =ADD_DAYS(A1; 10), где A1 содержит дату.
Добавление дней к дате в Google Calendar API
При создании или обновлении событий в Календаре часто нужно вычислить дату окончания на основе даты начала и длительности.
/**
* Создает событие в Google Calendar на N дней, начиная с завтрашнего дня.
*
* @param {string} calendarId ID календаря для добавления события.
* @param {string} eventTitle Название события.
* @param {number} durationDays Длительность события в днях.
*/
function createMultiDayEvent(calendarId: string, eventTitle: string, durationDays: number): void {
if (typeof durationDays !== 'number' || durationDays <= 0 || !Number.isInteger(durationDays)) {
Logger.log('Ошибка: Длительность должна быть положительным целым числом дней.');
return;
}
const tomorrow: Date = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
// Устанавливаем время на начало дня для события на весь день
tomorrow.setHours(0, 0, 0, 0);
const endDate: Date = new Date(tomorrow);
// Добавляем дни. Для событий 'all-day' конечная дата не включается,
// поэтому если событие длится N дней, конечная дата должна быть N дней спустя.
endDate.setDate(endDate.getDate() + durationDays);
// Даты для Calendar API лучше передавать в формате 'yyyy-MM-dd'
const startDateStr: string = Utilities.formatDate(tomorrow, Session.getScriptTimeZone(), 'yyyy-MM-dd');
const endDateStr: string = Utilities.formatDate(endDate, Session.getScriptTimeZone(), 'yyyy-MM-dd');
try {
const event = CalendarApp.getCalendarById(calendarId).createAllDayEvent(eventTitle, new Date(startDateStr), new Date(endDateStr));
Logger.log('Событие создано: ID %s, Начало: %s, Конец: %s', event.getId(), startDateStr, endDateStr);
} catch (e) {
Logger.log('Не удалось создать событие: %s', e);
}
}
// Пример вызова (замените 'primary' на нужный ID календаря)
// createMultiDayEvent('primary', 'Маркетинговая кампания X', 5);Обработка крайних случаев и ошибок
Надежный код всегда включает обработку потенциальных проблем.
Валидация входных данных (проверка на число дней)
Перед выполнением операций с датами всегда проверяйте, что количество добавляемых дней является корректным числом (целым или дробным, в зависимости от логики). Использование typeof, Number.isInteger или isNaN помогает избежать ошибок.
Обработка некорректных дат
Если исходная дата поступает из внешнего источника (например, ячейка таблицы, ввод пользователя), она может быть в неверном формате или вообще не быть датой. Используйте try...catch при создании объекта Date из таких данных и проверяйте валидность с помощью isNaN(date.getTime()).
/**
* Безопасно добавляет дни к дате, полученной из внешнего источника.
*
* @param {any} dateInput Потенциально некорректный ввод даты.
* @param {any} daysInput Потенциально некорректный ввод количества дней.
* @return {Date | null} Новая дата или null в случае ошибки.
*/
function safeAddDays(dateInput: any, daysInput: any): Date | null {
let startDate: Date;
let daysToAdd: number;
// Валидация количества дней
if (typeof daysInput !== 'number' || isNaN(daysInput)) {
console.error('Ошибка: Количество дней должно быть числом.');
return null;
}
daysToAdd = daysInput; // Можно добавить округление, если нужны целые дни
// Валидация и создание даты
try {
startDate = new Date(dateInput);
if (isNaN(startDate.getTime())) {
throw new Error('Некорректное значение даты.');
}
} catch (e) {
console.error('Ошибка обработки входной даты:', e.message);
return null;
}
// Выполнение операции
const resultDate = new Date(startDate);
resultDate.setDate(resultDate.getDate() + daysToAdd);
return resultDate;
}
// Пример вызова с обработкой
const inputDate = '2023-12-25';
const inputDays = '10'; // Строка, но будет приведена к числу
const result = safeAddDays(inputDate, Number(inputDays));
if (result) {
Logger.log('Успех! Новая дата: %s', result);
} else {
Logger.log('Не удалось добавить дни из-за ошибки входных данных.');
}
const invalidResult = safeAddDays('не дата', 5);
if (!invalidResult) {
Logger.log('Обработка ошибки некорректной даты прошла успешно.');
}Тщательная валидация и понимание особенностей работы с датами и часовыми поясами позволяют создавать надежные и предсказуемые скрипты в Google Apps Script.