📚

GAS開発者のための日付操作ガイド - GAS + TypeScriptによる日付操作ユーティリティの実装と応用

2025/01/18に公開

1. はじめに

  • GAS(Google Apps Script)のプロジェクトにおいて、日付の操作は頻繁に必要となる処理です。
  • 本記事では、GASとTypeScriptを使用した日付操作ユーティリティの実装について解説します。

2.日付操作ユーティリティ - 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()));
    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 addDays(baseDate: Date, days: number): string {
    return this.toFormatString(this.addDaysToDate(baseDate, days));
  }

  /**
   * 基準日から指定日数前の日付を文字列形式で取得します
   * @param baseDate 基準日
   * @param days 遡る日数
   * @returns yyyy/MM/dd形式の日付文字列
   */
  public static removeDays(baseDate: Date, days: number): string {
    return this.toFormatString(this.addDaysToDate(baseDate, -days));
  }

  /**
   * 日付を指定フォーマットの文字列に変換します。
   * @param 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() + 6));
    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(),
    };
  }
}

3. 主要メソッドの解説

3.1 日付の取得

DateUtilクラスは様々な日付取得メソッドを提供します。

// 7日前の日付を取得
const sevenDaysAgo = DateUtil.getDaysAgo(7);
console.log(sevenDaysAgo); // 例: 2025-01-11 00:00:00

// 来週の月曜日と日曜日を取得
const nextMonday = DateUtil.getNextMonday();
const nextSunday = DateUtil.getNextSunday();

// 来週の日付範囲を一度に取得
const nextWeekRange = DateUtil.getNextWeekRange();
console.log(nextWeekRange);
// 出力例: { startDate: 2025-01-20 00:00:00, endDate: 2025-01-26 00:00:00 }

3.2 日付操作と文字列に変換

日付の加算・減算を行い、文字列に変換するメソッドを提供します。

const baseDate = new Date('2025-01-18');

// 3日後の日付(文字列型)を取得
const afterThreeDays = DateUtil.addDays(baseDate, 3);
console.log(afterThreeDays); // 出力: "2025/01/21"

// 5日前の日付(文字列型)を取得
const beforeFiveDays = DateUtil.removeDays(baseDate, 5);
console.log(beforeFiveDays); // 出力: "2025/01/13"

3.3 日付の比較と検証

日付の比較や範囲チェックを行うメソッドを提供します。

// 日付範囲のチェック
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 date1 = new Date('2025-01-18');
const date2 = new Date('2025-01-18');
console.log(DateUtil.isSameDate(date1, date2)); // 出力: true

3.4 日付のフォーマット

日付を様々な形式に変換するメソッドを提供します。

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"

4. DateUtilクラスの使用例 - 週次売上データの分析

  • 本章では、DateUtilクラスを使用したデータ分析の具体的な実装方法について説明します。
  • スプレッドシートに記録された売上データを、日付範囲でフィルタリングし、集計する処理を実装例として紹介します。

分析対象データ

分析の対象となるスプレッドシートには、以下のような週次の売上データが記録されているとします。

日付 商品名 売上金額 担当者
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 田中

データ分析の実装

  • analyzeWeeklySales
    • 週次売上データの分析を実行し、集計結果を返却します。
  • filterByDateRange
    • 指定された期間内の売上データを抽出します。
  • analyzeSalesByProduct
    • 商品別の売上合計を集計します。
  • analyzeSalesByPerson
    • 担当者別の売上合計を集計します。
/**
* 週次売上データの分析を実行し、集計結果を返却します。
* 
* @returns 以下の3つの分析結果を含むオブジェクト
*   - lastWeek: 直近1週間の売上データ
*   - salesByProduct: 商品別の売上集計
*   - salesByPerson: 担当者別の売上集計
*/
function analyzeWeeklySales() {
 const sheet = SpreadsheetApp.getActiveSheet();
 const dataRange = sheet.getDataRange();
 const values = dataRange.getValues();

 // 2025/01/16(木)にGASを実行したとする
 const lastWeekRange = DateUtil.getLastWeekRange();
 
 // ヘッダー行を除いたデータ行を取得
 const dataRows = values.slice(1);
 
 return {
   lastWeek: 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 => {
   const rowDate = new Date(row[0]);
   return DateUtil.isDateInRange(rowDate, dateRange.startDate, dateRange.endDate);
 });
}

/**
* 商品別の売上合計を集計します。
* 
* @param dataRows 売上データの2次元配列(日付、商品名、売上金額、担当者)
* @returns 商品名をキー、売上合計を値とするオブジェクト
*/
function analyzeSalesByProduct(dataRows: any[][]) {
 const salesByProduct = {};
 dataRows.forEach(row => {
   const product = row[1];
   const amount = row[2];
   salesByProduct[product] = (salesByProduct[product] || 0) + amount;
 });
 return salesByProduct;
}

/**
* 担当者別の売上合計を集計します。
* 
* @param dataRows 売上データの2次元配列(日付、商品名、売上金額、担当者)
* @returns 担当者名をキー、売上合計を値とするオブジェクト
*/
function analyzeSalesByPerson(dataRows: any[][]) {
 const salesByPerson = {};
 dataRows.forEach(row => {
   const person = row[3];
   const amount = row[2];
   salesByPerson[person] = (salesByPerson[person] || 0) + amount;
 });
 return salesByPerson;
}

実行結果 - 2025/01/16(木)に実行した場合

先週のデータ抽出結果:analysis.lastWeek

日付 商品名 売上金額 担当者
2025/01/06 商品A 1,200 山田
2025/01/07 商品B 2,300 鈴木
2025/01/08 商品A 1,500 田中

商品別売上集計:analysis.salesByProduct

商品名 売上合計
商品A 4,500
商品B 4,400
商品C 3,000

担当者別売上集計:analysis.salesByPerson

担当者 売上合計
山田 4,200
鈴木 4,400
田中 3,300

以上のようにDateUtilクラスを使用することで、週次のセールスレポートの作成や、商品ごとの販売傾向の把握、担当者のパフォーマンス評価など、様々な分析に活用することができます。

5. まとめ

本記事では、ClaspとTypeScriptを使用したDateUtilクラスの紹介と活用方法について解説しました。主なポイントは以下の通りです。

  • DateUtilクラスの基本機能

    • 日付の取得や計算を行う各種メソッド
    • TypeScriptによる型安全な実装
    • 時刻のリセットと日付比較機能
  • 主要メソッド

    • getDaysAgo - 指定日数前の日付取得
    • getNextMonday/Sunday - 来週の月曜/日曜の取得
    • isDateInRange - 日付範囲のチェック機能
    • formatDateWithDayOfWeek - 曜日付き日付フォーマット
  • DateUtilクラスの使用例

    • スプレッドシートの週次データ分析
    • 日付範囲に基づくデータのフィルタリング
    • 商品別・担当者別の売上集計

最後までお読みいただき、ありがとうございました。

本記事が皆様のGAS開発のお役に立てば幸いです。

Discussion