GAS開発者のための日付操作ガイド - GAS + TypeScriptによる日付操作ユーティリティの実装と応用
GAS開発者のための日付操作ガイド - GAS + TypeScriptによる日付操作ユーティリティの実装と応用
1. はじめに
- GAS(Google Apps Script)のプロジェクトにおいて、日付の操作は頻繁に必要となる処理です。
- 本記事では、GASとTypeScriptを使用した日付操作ユーティリティの実装について解説します。
- 初学者の方にも理解しやすいよう、概念から実装、応用例まで順を追って説明します。
2. 準備:GASでTypeScriptを使うための環境設定
本記事で紹介するコードを実行するには、GAS開発環境にTypeScriptを導入する必要があります。
Clasp(Command Line Apps Script Projects)の設定
ClaspはGASプロジェクトをローカルで開発するためのツールです。以下の手順で設定します。
- Node.jsをインストール(未導入の場合)
- npmを使ってClaspをインストール
npm install -g @google/clasp - Googleアカウントでログイン
clasp login
TypeScriptプロジェクトの初期化方法については、Google公式ドキュメントを参照してください:TypeScript を使用する
3. JavaScriptにおける日付の基本概念
TypeScriptによる日付操作の実装に入る前に、JavaScriptにおける日付の扱い方について簡単に解説します。
Dateオブジェクトの基本
JavaScriptでは、Dateオブジェクトを使って日付と時刻を扱います。
// 現在の日時でDateオブジェクトを作成
const now = new Date();
// 特定の日付を指定してDateオブジェクトを作成(フォーマット:YYYY-MM-DD)
const specificDate = new Date('2025-01-18');
// 年、月、日を指定してDateオブジェクトを作成(注意:月は0から始まります)
const anotherDate = new Date(2025, 0, 18); // 2025年1月18日
日付の取得と設定
const date = new Date('2025-01-18');
// 日付情報の取得
const year = date.getFullYear(); // 2025
const month = date.getMonth() + 1; // 1(getMonthは0始まりなので+1する)
const day = date.getDate(); // 18
const dayOfWeek = date.getDay(); // 6(0:日曜, 1:月曜, ... 6:土曜)
// 日付の設定
date.setFullYear(2026); // 年を2026に変更
date.setMonth(1); // 月を2月(1)に変更
date.setDate(15); // 日を15日に変更
GASにおける日付操作の注意点
GASでは、JavaScriptの標準的な日付操作に加えて、Utilitiesクラスを使った独自の日付操作機能も提供されています。
// GAS独自の日付フォーマット関数
const formattedDate = Utilities.formatDate(
new Date(),
'Asia/Tokyo', // タイムゾーン
'yyyy/MM/dd HH:mm:ss' // フォーマットパターン
);
これらの基本的な知識を踏まえた上で、より実用的な日付操作ユーティリティを作成していきましょう。
4. 日付操作ユーティリティ - DateUtilクラスの実装
DateUtilクラスは、GASアプリケーションでよく使う日付操作をまとめたユーティリティクラスです。機能ごとに分けて解説します。
4.1 基本的な日付計算機能
日付に対して日数の加算・減算を行う基本的な機能を実装します。
/**
* 日付操作のユーティリティクラス
*/
export class DateUtil {
/**
* 基準日から指定日数を加算または減算した日付を取得します
* @param date 基準日
* @param days 加算する日数(負数の場合は減算)
* @param resetTime 時刻をリセットするかどうか(デフォルトはtrue)
* @returns 計算後の日付
*/
public static addDaysToDate(
date: Date,
days: number,
resetTime: boolean = true
): Date {
const resultDate = new Date(date);
resultDate.setDate(resultDate.getDate() + days);
if (resetTime) {
resultDate.setHours(0, 0, 0, 0);
}
return resultDate;
}
/**
* 現在日時から指定日数前の日付を取得します
* @param days 遡る日数
* @returns 指定日数前の日付
*/
public static getDaysAgo(days: number): Date {
return this.addDaysToDate(new Date(), -days);
}
}
ポイント解説
-
addDaysToDateメソッドは、基準日に対して日数を加減算します -
resetTimeパラメータを使うと、時刻部分を00:00:00にリセットできます(日付比較に便利) -
getDaysAgoメソッドは、現在から指定日数前の日付を簡単に取得できる便利メソッドです
4.2 曜日に基づく日付取得機能
週の開始日(月曜日)や終了日(日曜日)を取得する機能を実装します。
/**
* 来週の月曜日の日付を取得します
* @returns 来週月曜日の日付
*/
public static getNextMonday(): Date {
const today = new Date();
const nextMonday = new Date(today);
// 今日の曜日を基に来週の月曜日までの日数を計算
// getDay()は0が日曜、1が月曜...6が土曜を返す
nextMonday.setDate(today.getDate() + (8 - today.getDay()) % 7);
nextMonday.setHours(0, 0, 0, 0);
return nextMonday;
}
/**
* 来週日曜日の日付を取得します
* @returns 来週日曜日の日付
*/
public static getNextSunday(): Date {
const nextMonday = this.getNextMonday();
const nextSunday = new Date(nextMonday);
nextSunday.setDate(nextMonday.getDate() + 6);
nextSunday.setHours(0, 0, 0, 0);
return nextSunday;
}
/**
* 来週の日付範囲を取得します
* @returns 来週の月曜日と日曜日の日付
*/
public static getNextWeekRange(): { startDate: Date; endDate: Date } {
return {
startDate: this.getNextMonday(),
endDate: this.getNextSunday(),
};
}
/**
* 先週の月曜日の日付を取得します
* @returns 先週月曜日の日付
*/
public static getLastMonday(): Date {
const today = new Date();
const lastMonday = new Date(today);
// 今日の曜日に基づいて、先週の月曜日までの日数を計算
lastMonday.setDate(today.getDate() - (today.getDay() === 0 ? 6 : today.getDay() - 1) - 7);
lastMonday.setHours(0, 0, 0, 0);
return lastMonday;
}
/**
* 先週の日曜日の日付を取得します
* @returns 先週日曜日の日付
*/
public static getLastSunday(): Date {
const lastMonday = this.getLastMonday();
const lastSunday = new Date(lastMonday);
lastSunday.setDate(lastMonday.getDate() + 6);
lastSunday.setHours(0, 0, 0, 0);
return lastSunday;
}
/**
* 先週の日付範囲を取得します
* @returns 先週の月曜日と日曜日の日付
*/
public static getLastWeekRange(): { startDate: Date; endDate: Date } {
return {
startDate: this.getLastMonday(),
endDate: this.getLastSunday(),
};
}
ポイント解説
-
getDay()メソッドは、日曜日を0、月曜日を1とする数値を返します -
getNextMonday()は、現在の曜日を考慮して来週の月曜日までの日数を計算しています -
getLastMonday()は、現在の曜日から先週の月曜日までの日数を引き算しています - 日曜日(getDay() = 0)の場合、特別な処理が必要なことに注意しています
4.3 日付の文字列変換機能
日付をさまざまな形式の文字列に変換する機能を提供します。
/**
* 基準日から指定日数後の日付を文字列形式で取得します
* @param baseDate 基準日
* @param days 進める日数
* @returns yyyy/MM/dd形式の日付文字列
*/
public static formatAddDays(baseDate: Date, days: number): string {
return this.toFormatString(this.addDaysToDate(baseDate, days));
}
/**
* 基準日から指定日数前の日付を文字列形式で取得します
* @param baseDate 基準日
* @param days 遡る日数
* @returns yyyy/MM/dd形式の日付文字列
*/
public static formatRemoveDays(baseDate: Date, days: number): string {
return this.toFormatString(this.addDaysToDate(baseDate, -days));
}
/**
* 日付を指定フォーマットの文字列に変換します。
* @param date 変換する日付(JavaScriptのDateオブジェクトまたはGASのDateオブジェクト)
* @returns yyyy/MM/dd形式の日付文字列
*/
public static toFormatString(
date: Date | GoogleAppsScript.Base.Date
): string {
// GAS独自のUtilitiesクラスを使ってフォーマットします
return Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy/MM/dd');
}
/**
* 日付を「MM/DD (曜日)」形式でフォーマットします
* @param date フォーマットする日付
* @returns MM/DD (曜日) 形式の文字列
*/
public static formatDateWithDayOfWeek(date: Date): string {
const dayOfWeek = ['日', '月', '火', '水', '木', '金', '土'][date.getDay()];
return `${date.getMonth() + 1}/${date.getDate()} (${dayOfWeek})`;
}
/**
* 日付範囲を「MM/DD~MM/DD」形式でフォーマットします
* @param startDate 開始日
* @param endDate 終了日
* @returns MM/DD~MM/DD 形式の文字列
*/
public static formatDateRange(startDate: Date, endDate: Date): string {
return `${startDate.getMonth() + 1}/${startDate.getDate()}~${
endDate.getMonth() + 1
}/${endDate.getDate()}`;
}
ポイント解説
- 元の記事の
addDaysとremoveDaysメソッドは、機能が分かりやすいようにformatAddDaysとformatRemoveDaysに変更しました -
toFormatStringメソッドでは、GAS独自のUtilities.formatDate関数を使用しています -
GoogleAppsScript.Base.Date型は、GAS内部で使われる日付型で、JavaScriptの標準Date型も受け付けられるようにしています -
formatDateWithDayOfWeekメソッドで日本語の曜日表示を実装しています
4.4 日付の比較・検証機能
日付の比較や範囲チェックを行う機能を実装します。
/**
* 指定された日付が来週以降の日付かどうかを判定します
* @param date 判定対象の日付
* @returns 来週以降の日付の場合はtrue
*/
public static isDateAfterNextWeek(date: Date): boolean {
const nextMonday = this.getNextMonday();
return date >= nextMonday;
}
/**
* 2つの日付が同じ日かどうかを判定します
* @param date1 比較する日付1
* @param date2 比較する日付2
* @returns 同じ日の場合はtrue、それ以外はfalse
*/
public static isSameDate(date1: Date, date2: Date): boolean {
return (
date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate()
);
}
/**
* 指定された日付が範囲内かどうかを判定します
* @param date 判定する日付
* @param startDate 範囲開始日
* @param endDate 範囲終了日(この日を含む)
* @returns 範囲内の場合はtrue、それ以外はfalse
*/
public static isDateInRange(
date: Date,
startDate: Date,
endDate: Date
): boolean {
const normalizedDate = this.normalizeDate(date);
const normalizedStart = this.normalizeDate(startDate);
const normalizedEnd = this.normalizeDate(endDate);
return normalizedDate >= normalizedStart && normalizedDate <= normalizedEnd;
}
/**
* 日付の時刻部分を0時0分0秒に正規化します
* 日付比較を行う際、時刻部分を無視して純粋に日付だけを比較するために使用します
* @param date 正規化する日付
* @returns 時刻部分がリセットされた新しい日付オブジェクト
*/
private static normalizeDate(date: Date): Date {
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
}
ポイント解説
-
isSameDateメソッドは、日付部分のみを比較し時刻は無視します -
normalizeDateはプライベートメソッドで、時刻部分をリセットした新しい日付オブジェクトを作成します - 日付比較では、JavaScriptの標準比較演算子(
>=,<=など)が利用できます - 日付範囲チェックでは、開始日と終了日を含む範囲でチェックしています
5. DateUtilクラスの使い方 - 基本編
ここでは、DateUtilクラスのメソッドを使った基本的な日付操作の例を紹介します。
5.1 日付の取得と計算
// 実行日から7日前の日付を取得
const sevenDaysAgo = DateUtil.getDaysAgo(7);
console.log("7日前の日付:", DateUtil.toFormatString(sevenDaysAgo));
// 出力例: 7日前の日付: 2025/01/11
// 来週の日付範囲を取得
const nextWeekRange = DateUtil.getNextWeekRange();
console.log("来週の期間:",
DateUtil.toFormatString(nextWeekRange.startDate) + " から " +
DateUtil.toFormatString(nextWeekRange.endDate)
);
// 出力例: 来週の期間: 2025/01/20 から 2025/01/26
// 特定の日付から3日後を計算
const baseDate = new Date('2025-01-18');
const afterThreeDays = DateUtil.formatAddDays(baseDate, 3);
console.log("3日後の日付:", afterThreeDays);
// 出力例: 3日後の日付: 2025/01/21
5.2 日付の比較とフォーマット
// 日付範囲チェック
const targetDate = new Date('2025-01-20');
const startDate = new Date('2025-01-18');
const endDate = new Date('2025-01-25');
const isInRange = DateUtil.isDateInRange(targetDate, startDate, endDate);
console.log("期間内か:", isInRange); // 出力例: 期間内か: true
// 日付の曜日付きフォーマット
const date = new Date('2025-01-18');
const formattedDate = DateUtil.formatDateWithDayOfWeek(date);
console.log("フォーマット済み日付:", formattedDate);
// 出力例: フォーマット済み日付: 1/18 (土)
// 日付範囲のフォーマット
const start = new Date('2025-01-20');
const end = new Date('2025-01-26');
const range = DateUtil.formatDateRange(start, end);
console.log("期間表示:", range);
// 出力例: 期間表示: 1/20~1/26
5.3 よくあるエラーと対処法
DateUtilクラスを使用する際によく起こるエラーとその対処法を紹介します。
1. 日付が意図しない値になる
// 問題のコード
const date = new Date('2025/1/32'); // 無効な日付
console.log(date); // 出力例: 2025-02-01 ...
// 解決策:日付の妥当性チェック
function isValidDate(date) {
return date instanceof Date && !isNaN(date.getTime());
}
const testDate = new Date('2025/1/32');
if (!isValidDate(testDate)) {
console.log("無効な日付です");
}
2. 月の表記に関する注意
// 問題のコード
const date = new Date(2025, 1, 1);
console.log(DateUtil.toFormatString(date)); // 出力例: 2025/02/01(2月になる)
// 解決策:月は0始まりであることを意識する
const januaryDate = new Date(2025, 0, 1); // 1月を表すには0を指定
console.log(DateUtil.toFormatString(januaryDate)); // 出力例: 2025/01/01
3. タイムゾーンの問題
// 問題のコード
const date = new Date('2025-01-18T00:00:00');
console.log(date); // ブラウザの環境によっては時差が適用される
// 解決策:GASのUtilities.formatDateを使用
const formattedDate = Utilities.formatDate(
date,
'Asia/Tokyo', // タイムゾーンを明示的に指定
'yyyy/MM/dd HH:mm:ss'
);
console.log(formattedDate); // 出力例: 2025/01/18 00:00:00
6. DateUtilクラスの応用例 - 週次売上データの分析
DateUtilクラスを使った実践的な応用例として、スプレッドシートの週次売上データを分析するアプリケーションを作成します。
6.1 アプリケーションの目的と概要
- 目的:週次の売上データを日付範囲でフィルタリングし、商品別・担当者別に集計する
- 入力:スプレッドシートに記録された売上データ
- 出力:指定期間の売上データ、商品別集計、担当者別集計
6.2 分析対象データ
スプレッドシートには以下のような形式で売上データが記録されています。
| 日付 | 商品名 | 売上金額 | 担当者 |
|---|---|---|---|
| 2025/01/06 | 商品A | 1,200 | 山田 |
| 2025/01/07 | 商品B | 2,300 | 鈴木 |
| 2025/01/08 | 商品A | 1,500 | 田中 |
| 2025/01/13 | 商品C | 3,000 | 山田 |
| 2025/01/14 | 商品B | 2,100 | 鈴木 |
| 2025/01/15 | 商品A | 1,800 | 田中 |
6.3 実装コード
/**
* アプリケーションのエントリーポイント
* 手動実行またはトリガーから呼び出されます
*/
function runSalesAnalysis() {
const analysis = analyzeWeeklySales();
displayResults(analysis);
}
/**
* 週次売上データの分析を実行し、集計結果を返却します。
*
* @returns 以下の3つの分析結果を含むオブジェクト
* - lastWeekData: 先週の売上データ
* - salesByProduct: 商品別の売上集計
* - salesByPerson: 担当者別の売上集計
*/
function analyzeWeeklySales() {
// スプレッドシートからデータを取得
const sheet = SpreadsheetApp.getActiveSheet();
const dataRange = sheet.getDataRange();
const values = dataRange.getValues();
// 先週の日付範囲を取得
const lastWeekRange = DateUtil.getLastWeekRange();
// ヘッダー行を除いたデータ行を取得
const dataRows = values.slice(1);
// 分析結果を返却
return {
lastWeekData: filterByDateRange(dataRows, lastWeekRange),
salesByProduct: analyzeSalesByProduct(dataRows),
salesByPerson: analyzeSalesByPerson(dataRows)
};
}
/**
* 指定された期間内の売上データを抽出します。
*
* @param dataRows 売上データの2次元配列(日付、商品名、売上金額、担当者)
* @param dateRange 抽出対象期間(開始日と終了日を含むオブジェクト)
* @returns 指定期間内の売上データ
*/
function filterByDateRange(dataRows: any[][], dateRange: { startDate: Date; endDate: Date }) {
return dataRows.filter(row => {
// 文字列の日付をDateオブジェクトに変換
const rowDateStr = row[0]; // 例: "2025/01/06"
let rowDate: Date;
// 日付がすでにDateオブジェクトの場合とそうでない場合を処理
if (rowDateStr instanceof Date) {
rowDate = rowDateStr;
} else {
// 日付文字列からDateオブジェクトを作成
rowDate = new Date(rowDateStr);
// 無効な日付の場合はスキップ
if (isNaN(rowDate.getTime())) {
console.log(`無効な日付形式: ${rowDateStr}`);
return false;
}
}
// 指定された日付範囲内かどうかをチェック
return DateUtil.isDateInRange(rowDate, dateRange.startDate, dateRange.endDate);
});
}
/**
* 商品別の売上合計を集計します。
*
* @param dataRows 売上データの2次元配列(日付、商品名、売上金額、担当者)
* @returns 商品名をキー、売上合計を値とするオブジェクト
*/
function analyzeSalesByProduct(dataRows: any[][]) {
const salesByProduct = {};
dataRows.forEach(row => {
const productName = row[1]; // 商品名
const salesAmount = parseFloat(String(row[2]).replace(/,/g, '')); // カンマを除去して数値化
// 無効な金額はスキップ
if (isNaN(salesAmount)) {
console.log(`無効な売上金額: ${row[2]} (商品: ${productName})`);
return;
}
// 商品ごとの売上を集計
salesByProduct[productName] = (salesByProduct[productName] || 0) + salesAmount;
});
return salesByProduct;
}
/**
* 担当者別の売上合計を集計します。
*
* @param dataRows 売上データの2次元配列(日付、商品名、売上金額、担当者)
* @returns 担当者名をキー、売上合計を値とするオブジェクト
*/
function analyzeSalesByPerson(dataRows: any[][]) {
const salesByPerson = {};
dataRows.forEach(row => {
const personName = row[3]; // 担当者名
const salesAmount = parseFloat(String(row[2]).replace(/,/g, '')); // カンマを除去して数値化
// 無効な金額はスキップ
if (isNaN(salesAmount)) {
console.log(`無効な売上金額: ${row[2]} (担当者: ${personName})`);
return;
}
// 担当者ごとの売上を集計
salesByPerson[personName] = (salesByPerson[personName] || 0) + salesAmount;
});
return salesByPerson;
}
/**
* 分析結果をシートに表示する
* @param analysis 分析結果オブジェクト
*/
function displayResults(analysis) {
// 結果表示用の新しいシートを作成(既に存在する場合は取得)
let resultSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('分析結果');
if (!resultSheet) {
resultSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet('分析結果');
} else {
resultSheet.clear(); // 既存のデータをクリア
}
// 先週の日付範囲を取得(見出し用)
const lastWeekRange = DateUtil.getLastWeekRange();
const rangeTitle = `${DateUtil.formatDateWithDayOfWeek(lastWeekRange.startDate)}~${DateUtil.formatDateWithDayOfWeek(lastWeekRange.endDate)}の分析結果`;
// タイトルを設定
resultSheet.getRange(1, 1).setValue(rangeTitle);
resultSheet.getRange(1, 1).setFontWeight('bold');
// セクション1: 先週のデータ
resultSheet.getRange(3, 1).setValue('1. 先週の売上データ');
resultSheet.getRange(3, 1).setFontWeight('bold');
// ヘッダー
resultSheet.getRange(4, 1, 1, 4).setValues([['日付', '商品名', '売上金額', '担当者']]);
resultSheet.getRange(4, 1, 1, 4).setFontWeight('bold');
// データがあれば表示
if (analysis.lastWeekData.length > 0) {
// 日付をフォーマット
const formattedData = analysis.lastWeekData.map(row => {
const date = row[0] instanceof Date ? row[0] : new Date(row[0]);
return [
DateUtil.formatDateWithDayOfWeek(date),
row[1], // 商品名
row[2], // 売上金額
row[3] // 担当者
];
});
resultSheet.getRange(5, 1, formattedData.length, 4).setValues(formattedData);
} else {
resultSheet.getRange(5, 1).setValue('該当するデータはありません');
}
// セクション2: 商品別集計
resultSheet.getRange(7 + (analysis.lastWeekData.length || 1), 1).setValue('2. 商品別売上集計');
resultSheet.getRange(7 + (analysis.lastWeekData.length || 1), 1).setFontWeight('bold');
const productRow = 8 + (analysis.lastWeekData.length || 1);
resultSheet.getRange(productRow, 1, 1, 2).setValues([['商品名', '売上合計']]);
resultSheet.getRange(productRow, 1, 1, 2).setFontWeight('bold');
const productData = Object.entries(analysis.salesByProduct).map(([product, sales]) => [product, sales]);
resultSheet.getRange(productRow + 1, 1, productData.length, 2).setValues(productData);
// セクション3: 担当者別集計
resultSheet.getRange(productRow + 3 + productData.length, 1).setValue('3. 担当者別売上集計');
resultSheet.getRange(productRow + 3 + productData.length, 1).setFontWeight('bold');
const personRow = productRow + 4 + productData.length;
resultSheet.getRange(personRow, 1, 1, 2).setValues([['担当者', '売上合計']]);
resultSheet.getRange(personRow, 1, 1, 2).setFontWeight('bold');
const personData = Object.entries(analysis.salesByPerson).map(([person, sales]) => [person, sales]);
resultSheet.getRange(personRow + 1, 1, personData.length, 2).setValues(personData);
// シートの列幅を自動調整
resultSheet.autoResizeColumns(1, 4);
// 完了メッセージを表示
SpreadsheetApp.getUi().alert('分析結果を「分析結果」シートに出力しました。');
}
6.4 実装上のポイント
-
エラーハンドリング
- 無効な日付や数値の処理
- データ不足の場合の対応
-
パフォーマンス最適化
- 日付処理における時刻のリセット
- 繰り返し処理を最小限に抑える
-
ユーザーインターフェース
- 結果を見やすく整形
- 日付に曜日を追加して可読性を向上
-
メンテナンス性
- 機能ごとに関数を分割
- 明確な変数名と豊富なコメント
6.5 実行方法とカスタマイズ
この分析ツールは、次のいずれかの方法で実行できます。
-
手動実行:
- スプレッドシートのメニューから「ツール」→「スクリプトエディタ」を開き、
runSalesAnalysis関数を実行
- スプレッドシートのメニューから「ツール」→「スクリプトエディタ」を開き、
-
定期実行:
- トリガーを設定して毎週自動実行
function createWeeklyTrigger() { ScriptApp.newTrigger('runSalesAnalysis') .timeBased() .onWeekDay(ScriptApp.WeekDay.MONDAY) .atHour(9) .create(); } -
カスタムメニュー:
- スプレッドシートにカスタムメニューを追加して実行
function onOpen() { SpreadsheetApp.getUi() .createMenu('売上分析') .addItem('週次分析を実行', 'runSalesAnalysis') .addToUi(); }
7. DateUtilクラスの全コード
参考のため、DateUtilクラスの完全なコードを以下に示します。
/**
* 日付操作のユーティリティクラス
*/
export class DateUtil {
/**
* 基準日から指定日数を加算または減算した日付を取得します
* @param date 基準日
* @param days 加算する日数(負数の場合は減算)
* @param resetTime 時刻をリセットするかどうか(デフォルトはtrue)
* @returns 計算後の日付
*/
public static addDaysToDate(
date: Date,
days: number,
resetTime: boolean = true
): Date {
const resultDate = new Date(date);
resultDate.setDate(resultDate.getDate() + days);
if (resetTime) {
resultDate.setHours(0, 0, 0, 0);
}
return resultDate;
}
/**
* 現在日時から指定日数前の日付を取得します
* @param days 遡る日数
* @returns 指定日数前の日付
*/
public static getDaysAgo(days: number): Date {
return this.addDaysToDate(new Date(), -days);
}
/**
* 来週の月曜日の日付を取得します
* @returns 来週月曜日の日付
*/
public static getNextMonday(): Date {
const today = new Date();
const nextMonday = new Date(today);
nextMonday.setDate(today.getDate() + (8 - today.getDay()) % 7);
nextMonday.setHours(0, 0, 0, 0);
return nextMonday;
}
/**
* 来週日曜日の日付を取得します
* @returns 来週日曜日の日付
*/
public static getNextSunday(): Date {
const nextMonday = this.getNextMonday();
const nextSunday = new Date(nextMonday);
nextSunday.setDate(nextMonday.getDate() + 6);
nextSunday.setHours(0, 0, 0, 0);
return nextSunday;
}
/**
* 来週の日付範囲を取得します
* @returns 来週の月曜日と日曜日の日付
*/
public static getNextWeekRange(): { startDate: Date; endDate: Date } {
return {
startDate: this.getNextMonday(),
endDate: this.getNextSunday(),
};
}
/**
* 指定された日付が来週以降の日付かどうかを判定します
* @param date 判定対象の日付
* @returns 来週以降の日付の場合はtrue
*/
public static isDateAfterNextWeek(date: Date): boolean {
const nextMonday = this.getNextMonday();
return date >= nextMonday;
}
/**
* 基準日から指定日数後の日付を文字列形式で取得します
* @param baseDate 基準日
* @param days 進める日数
* @returns yyyy/MM/dd形式の日付文字列
*/
public static formatAddDays(baseDate: Date, days: number): string {
return this.toFormatString(this.addDaysToDate(baseDate, days));
}
/**
* 基準日から指定日数前の日付を文字列形式で取得します
* @param baseDate 基準日
* @param days 遡る日数
* @returns yyyy/MM/dd形式の日付文字列
*/
public static formatRemoveDays(baseDate: Date, days: number): string {
return this.toFormatString(this.addDaysToDate(baseDate, -days));
}
/**
* 日付を指定フォーマットの文字列に変換します。
* @param date JavaScriptのDateオブジェクトまたはGASのDateオブジェクト
* @returns yyyy/MM/dd形式の日付文字列
*/
public static toFormatString(
date: Date | GoogleAppsScript.Base.Date
): string {
return Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy/MM/dd');
}
/**
* 2つの日付が同じ日かどうかを判定します
* @param date1 比較する日付1
* @param date2 比較する日付2
* @returns 同じ日の場合はtrue、それ以外はfalse
*/
public static isSameDate(date1: Date, date2: Date): boolean {
return (
date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate()
);
}
/**
* 指定された日付が範囲内かどうかを判定します
* @param date 判定する日付
* @param startDate 範囲開始日
* @param endDate 範囲終了日(この日を含む)
* @returns boolean
*/
public static isDateInRange(
date: Date,
startDate: Date,
endDate: Date
): boolean {
const normalizedDate = this.normalizeDate(date);
const normalizedStart = this.normalizeDate(startDate);
const normalizedEnd = this.normalizeDate(endDate);
return normalizedDate >= normalizedStart && normalizedDate <= normalizedEnd;
}
/**
* 日付の時刻部分を0時0分0秒に正規化します
* 日付比較を行う際、時刻部分を無視して純粋に日付だけを比較するために使用します
*/
private static normalizeDate(date: Date): Date {
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
}
/**
* 日付を「MM/DD (曜日)」形式でフォーマットします
* @param date フォーマットする日付
* @returns MM/DD (曜日) 形式の文字列
*/
public static formatDateWithDayOfWeek(date: Date): string {
const dayOfWeek = ['日', '月', '火', '水', '木', '金', '土'][date.getDay()];
return `${date.getMonth() + 1}/${date.getDate()} (${dayOfWeek})`;
}
/**
* 日付範囲を「MM/DD~MM/DD」形式でフォーマットします
* @param startDate 開始日
* @param endDate 終了日
* @returns MM/DD~MM/DD 形式の文字列
*/
public static formatDateRange(startDate: Date, endDate: Date): string {
return `${startDate.getMonth() + 1}/${startDate.getDate()}~${
endDate.getMonth() + 1
}/${endDate.getDate()}`;
}
/**
* 先週の月曜日の日付を取得します
* @returns 先週月曜日の日付
*/
public static getLastMonday(): Date {
const today = new Date();
const lastMonday = new Date(today);
lastMonday.setDate(today.getDate() - (today.getDay() === 0 ? 6 : today.getDay() - 1) - 7);
lastMonday.setHours(0, 0, 0, 0);
return lastMonday;
}
/**
* 先週の日曜日の日付を取得します
* @returns 先週日曜日の日付
*/
public static getLastSunday(): Date {
const lastMonday = this.getLastMonday();
const lastSunday = new Date(lastMonday);
lastSunday.setDate(lastMonday.getDate() + 6);
lastSunday.setHours(0, 0, 0, 0);
return lastSunday;
}
/**
* 先週の日付範囲を取得します
* @returns 先週の月曜日と日曜日の日付
*/
public static getLastWeekRange(): { startDate: Date; endDate: Date } {
return {
startDate: this.getLastMonday(),
endDate: this.getLastSunday(),
};
}
}
8. メソッド一覧表
DateUtilクラスの各メソッドを機能別にまとめた一覧表です。
| メソッド名 | 戻り値の型 | 機能説明 | 使用例 |
|---|---|---|---|
| 日付計算系 | |||
| addDaysToDate | Date | 基準日から指定日数を加算/減算 | DateUtil.addDaysToDate(date, 3) |
| getDaysAgo | Date | 現在から指定日数前の日付 | DateUtil.getDaysAgo(7) |
| 週関連 | |||
| getNextMonday | Date | 来週の月曜日を取得 | DateUtil.getNextMonday() |
| getNextSunday | Date | 来週の日曜日を取得 | DateUtil.getNextSunday() |
| getNextWeekRange | Object | 来週の日付範囲を取得 | DateUtil.getNextWeekRange() |
| getLastMonday | Date | 先週の月曜日を取得 | DateUtil.getLastMonday() |
| getLastSunday | Date | 先週の日曜日を取得 | DateUtil.getLastSunday() |
| getLastWeekRange | Object | 先週の日付範囲を取得 | DateUtil.getLastWeekRange() |
| 文字列変換系 | |||
| formatAddDays | string | 基準日から指定日数後の日付を文字列で取得 | DateUtil.formatAddDays(date, 3) |
| formatRemoveDays | string | 基準日から指定日数前の日付を文字列で取得 | DateUtil.formatRemoveDays(date, 5) |
| toFormatString | string | 日付をYYYY/MM/DD形式に変換 | DateUtil.toFormatString(date) |
| formatDateWithDayOfWeek | string | 日付をMM/DD (曜日)形式で取得 | DateUtil.formatDateWithDayOfWeek(date) |
| formatDateRange | string | 日付範囲をMM/DD~MM/DD形式で取得 | DateUtil.formatDateRange(start, end) |
| 比較検証系 | |||
| isDateAfterNextWeek | boolean | 指定日が来週以降かどうか判定 | DateUtil.isDateAfterNextWeek(date) |
| isSameDate | boolean | 2つの日付が同じ日かどうか判定 | DateUtil.isSameDate(date1, date2) |
| isDateInRange | boolean | 指定日が日付範囲内かどうか判定 | DateUtil.isDateInRange(date, start, end) |
| 内部メソッド | |||
| normalizeDate | Date | 日付の時刻部分を0時0分0秒に正規化 | (内部使用) |
9. まとめ
本記事では、GASとTypeScriptを使った日付操作ユーティリティ「DateUtil」クラスの実装と活用方法について解説しました。
主なポイント
-
日付操作の基本と応用
- JavaScriptのDateオブジェクトの基本
- GAS特有の日付操作機能
- 実務で役立つメソッドの実装
-
コードの可読性と再利用性
- TypeScriptによる型安全な実装
- 一貫した命名規則
- 豊富なコメントとドキュメント
-
実践的な応用例
- 週次データの分析
- スプレッドシートとの連携
- エラーハンドリングの実装
活用シーン
- 定期レポート作成:週次、月次の売上集計や業務レポートの自動化
- スケジュール管理:日付範囲に基づくタスク管理や予定表作成
- データ分析:特定期間のデータ抽出と集計
- カレンダー連携:Googleカレンダーへのイベント登録自動化
DateUtilクラスを使いこなすことで、GASアプリケーションの開発効率が大幅に向上します。本記事で紹介したコードやテクニックを、ぜひ皆様のプロジェクトにお役立てください。
最後までお読みいただき、ありがとうございました。
Discussion