
こんにちは、etau です 🤓
Google Apps Script (GAS) で、最初に new するオブジェクトといえば、Date オブジェクトですが (?)、 使い勝手がイマイチなところがあります。
思いついたものをいくつか書き出してみます。
- 同じ日時の Date オブジェクトの比較が、厳密等価演算子
===
, 等価演算子==
でできない - yyyy/MM/dd の形式で値を取りたいだけなのに、コードが長くなってしまう
- 月 (Month) の値がなぜか 0 から 11 までで取得される
このような問題に対応するために、かゆいところに手が届く独自クラスを作っていきます。
といいながら、まずは独自クラスを使わない方法をご紹介します。
ライブラリを使うという選択肢
Google Apps Script にはライブラリという機能があります。
この機能を使うことによって、自分で作った関数や、別の方が公開している関数を利用することができます。
とっても、ありがたいですね。
Date オブジェクト関連のライブラリでいうと、この 2 つが有名です。
後者の Moment.js については、すでに開発が停止されているため注意が必要です。
これらのライブラリを使うことによって Date オブジェクトにたいする不満は、ほぼ解消されると思います。
しかしながら、Moment.js のようにライブラリの開発が終わってしまった場合に、そのライブラリを利用していたプログラムを探し出して、リファクタリングするつらい経験をしてしまうと、使う機能だけ自分で作ってしまったほうが、いいんじゃない?ってなってしまうわけです。
では、1 つ目の問題点から確認していきます。
Date オブジェクトの比較
では、まず最初の問題点「日時の比較」ができない問題です。
コードの例で確認していきましょう。
function myFunction() { const date1 = new Date('2022/02/22'); const date2 = new Date('2022/02/22'); console.log(date1 === date2); // false console.log(date1 == date2); // false }
このように、オブジェクトの特性を理解していないと目を疑うような結果になります。
このオブジェクト (とプリミティブ型) に対しての考察はこちらの記事がたいへん参考になります。

この問題への対処法としては、特定のメソッドを使って比較する方法があります。
function myFunction() { const date1 = new Date('2022/02/22'); const date2 = new Date('2022/02/22'); console.log(date1.getTime() === date2.getTime()); // true }
getTime メソッド以外にも toString メソッドなど、複数の選択肢があります
それでは、次の問題点「フォーマットするためのコードが長い」です。
Utilities クラスを利用したフォーマット
Date オブジェクトを、利用したい文字列型に変更するためには、Date オブジェクトのメソッドではなく、GAS の Utilities クラスにある formatDate メソッドを利用します。
以下で確認できます。
function myFunction() { const date = new Date('2022/02/22'); const strDate = Utilities.formatDate(date, 'JST', 'yyyy/MM/dd'); console.log(date); // Tue Feb 22 2022 00:00:00 GMT+0900 (Japan Standard Time) console.log(strDate); // 2022/02/22 }
Utilities クラスの formatDate メソッドの第 2 引数の timeZone には 'JST'
と 'Asia/Tokyo'
のいずれかを選択することで日本標準時の値が取れます
Date オブジェクトのメソッド単体では、なかなか思うような処理ができず、以下の strDate1
のように、無理やり表示させる必要があります。
function myFunction() { const date = new Date('2022/02/22'); const strDate1 = date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate(); console.log(strDate1); // 2022/2/22 NOTE: この値を取得したい const strDate2 = date.toDateString(); console.log(strDate2); // Tue Feb 22 2022 const strDate3 = date.toLocaleDateString(); console.log(strDate3); // 2/22/2022 }
この書き方に比べれば formatDate メソッドはとても優秀です。
すこしクラス名が長くて、すこしメソッド名が長くて、すこし仮引数が多い点をのぞけば。
また、最後の問題点「月数が 0 – 11」が、ちょうどこの strDate1 のところで出現しています。
1 を加算することによって問題なくあつかえますが、たとえば「3 月」かどうかの判定をおこなうために date.getMonth() === 3
のように書いてしまい、エラーに悩まされた経験は、みなさんあると思います。
今回は、以上 3 点の問題点をクリアにするクラスを書いていきます。
クラス
実装するクラスのメンバー (プロパティとメソッド) について、かんたんに解説します。
メンバーの設定
メンバー種別 | メンバー名 | メンバーの内容 |
---|---|---|
プロパティ | date | Date オブジェクト |
メソッド | isSame | 日時を比較するメソッド |
メソッド | getMonth | 月数 (1 – 12) を返すメソッド |
メソッド | setMonth | 月数 (1 – 12) を設定するメソッド |
メソッド | toString | date プロパティを指定の文字列にフォーマットするメソッド |
静的メソッド | format | 指定の文字列にフォーマットする静的メソッド |
今回は問題となった getMonth, setMonth のみ Date オブジェクトから手を加えて委譲しましたが、クラス内での利用や、クライアントでの利用状況に応じて getFullYear や getDate などを追加して、便利にご利用ください
クラス名は Datetime でいきます。
Datetime クラスのコード
'use strict' class Datetime { /** * 日時に関するコンストラクタ * @constructor * @param {Date|string|number} param - Date オブジェクトでインスタンス生成可能な引数 */ constructor(param = new Date()) { /** @type {Date} */ this.date = new Date(param); } /** * Date オブジェクトから委譲されたメソッド * NOTE: getMonth, setMonth の戻り値、仮引数は 0 - 11 を 1 - 12 に変更 */ getMonth() { return this.date.getMonth() + 1; } setMonth(arg) { return this.date.setMonth(arg - 1); } /** * format 部分が同じものか比較するメソッド * @param {Date} date - 比較対象の Date オブジェクト * @param {string} format - 比較するフォーマット * @return {boolean} format 部分が同じかどうか */ isSame(date, format = 'yyyy/MM/dd HH:mm:ss') { return Datetime.format(date, format) === Datetime.format(this.date, format); } /** * コンストラクタの date オブジェクトを指定のフォーマットで文字列化するメソッド * @param {string} format - フォーマットする形式 * @return {string} フォーマットされた文字列型の日時 */ toString(format = 'yyyy/MM/dd HH:mm:ss') { const strDate = Datetime.format(this.date, format); return strDate; } /** * 指定のフォーマットで日時を文字列化する静的メソッド * @param {Date} d - Date オブジェクト 文字列型も可 * @param {string} format - フォーマットする形式 * @return {string} フォーマットされた文字列型の日時 */ static format(d = new Date(), format = 'yyyy/MM/dd HH:mm:ss') { const date = new Date(d); const strDate = Utilities.formatDate(date, 'JST', format); return strDate; } }
今回は、手続き型でコーディングしていた部分と異なる点が多いので、もう少し詳しくメンバーについて説明していきます。
- 唯一のプロパティ date プロパティは、Date オブジェクトが受けることができる仮引数のパターンすべてを受け、その値を new Date() した値を持ちます
- getMonth, setMonth メソッドは、対応する値が 0 – 11 だったものを 1 – 12 に変更しました
- isSame メソッドは、仮引数として受ける Date オブジェクトと第 2 引数のフォーマット形式部分のみを静的メソッド format を使って比較した結果を返します
※ 初期設定でミリ秒の比較はしません - toString メソッドは、静的メソッド format を使って、date プロパティを仮引数のフォーマット形式の文字列型に変更します
- 静的メソッド format は第 1 引数の Date オブジェクトを第 2 引数のフォーマット形式の文字列型にして返します
今回 format のデフォルト引数は yyyy/MM/dd HH:mm:ss に設定してありますが、プログラム内で頻出するフォーマットに変更すると、さらにクラスを便利に利用できます
では、このクラスを実際に使ったコード例です。
function myFunction() { const dt = new Datetime('2022/02/22'); console.log(dt); // { date: Tue Feb 22 2022 00:00:00 GMT+0900 (Japan Standard Time) } console.log(dt.getMonth()); // 2 dt.setMonth(5); console.log(dt.toString()); // 2022/05/22 00:00:00 const date1 = new Date('2022/05/22'); console.log(dt.isSame(date1)); // true console.log(Datetime.format(date1, 'yyyy/MM/dd')); // 2022/05/22 const date2 = new Date('2022/02/22'); console.log(dt.isSame(date2)); // false console.log(dt.isSame(date2, 'yyyy')) // true NOTE: 年 (4 桁) 部分を比較 }
今回問題点としてあげた 3 点は、すべてクリアになりました。
GitHub リンク
まとめ
さて、今回の記事はいかがでしたでしょうか。
とりまわしにコツのいる Date オブジェクトを独自クラスのプロパティに持たせることによって、必要なメソッドのみ Date オブジェクトから引き継ぎ、足りないメソッドをくわえることで、とても便利な Datetime オブジェクトが完成しました。
独自クラスのメリットが少しでも伝われば、嬉しいです。
また、弊社のコーディング ガイドラインでは 1 クラス 1 gs ファイルとしているので「あ、Date オブジェクト使うから、このクラス持ってこよう」くらいの軽い感じで、クラスを使いまわします。
今回は書かれていないメリットもあわせて以下の記事で読んでいただけると幸いです。

今後の記事では Google Workspace services のクラスについても、便利な独自クラスの利用例を紹介していければと思います。