【GAS】YYYY/MM/DD/hh/mm/ssからUNIXタイムスタンプを生成する

GASで**引数に日時を取るスラッシュコマンド**を作るにあたって、「スラッシュコマンドで指定する日付と時刻はわかりやすい**YYYY/MM/DD/hh/mm/ss**がいい。でも**Slack APIを叩く時はUNIXタイムスタンプで指定しないといけない**」という都合により、GASで「YYYY/MM/DD/hh/mm/ss→UNIXタイムスタンプ」の変換をすることになったんですが、思いの外ネット上にベストマッチなソリューションが見当たらなかったので書き残しておきます。

#実装①

```js
//2020年1月1日12時34分56秒
var str = "2020/01/01/12/34/56"
var array = str.split(/\//).map(Number)
var date = new Date(array[0], array[1] - 1, array[2], array[3], array[4], array[5])
date = (date.getTime() / 1000) + ''
Logger.log(date) // -> 1577849696
```

#実装②

```js
var str = "2020/01/01/12/34/56"
var array = str.split(/\//)
var date = Utilities.formatString("%s-%s-%sT%s:%s:%s+09:00", array[0], array[1], array[2], array[3], array[4], array[5])
date = Date.parse(date) / 1000 + ''
Logger.log(date) // -> 1577849696
```

#確認
[UNIX時間⇒日付変換 - 高精度計算サイト - Keisan - CASIO](https://keisan.casio.jp/exec/system/1526004418)
![スクリーンショット (47).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507842/da685f7b-428f-81f3-9ead-279309851c98.png)

#1000で割る意味
`getTime()`や`Date.parse()`によって返ってくるUNIX時は単位がミリ秒になっています。Slack APIで指定するタイムスタンプは秒単位でなければならないので、1000で割った値を文字列に変換しています。

#実装①のメリデメ
**メリット**：

 - 一度数値に変換するので、引数のバリデーションとかゼロ埋めとか気にしなくていい（？）
 - 短くて読みやすい（？）

**デメリット**：

 - `Date`オブジェクトを経由するのが煩わしくないこともない
 - `Date`クラスのメソッドの仕様が厄介

デメリットの方について少し説明しますと、数列からDateオブジェクトを生成する`new Date()`の構文は

```js
new Date(year, monthIndex [, day [, hours [, minutes [, seconds [, milliseconds]]]]]);
```

なのですが、この`monthIndex`は0から11で指定するんですね。

>注: 引数 monthIndex は 0 から始まります。 つまり 1 月 = 0、 12 月 = 11 です。

[Date - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date)

なので、`new Date()`に渡す時にそこだけ`- 1`する必要があります。

あと、公式マニュアルを見返していて気付いたんですが（強調は筆者）、

>注: Date を複数の引数を伴ってコンストラクタとして呼び出された場所では、**値が論理範囲より大きくても (month 値に 13 を与えたり、minute 値に 70 を与える)、調整された値になります**。つまり、new Date(2013, 13, 1) は、new Date(2014, 1, 1) と等しくなるように調整され、両者とも 2014-02-01 の日付を生成します

**……何…… ……だと……**（画像略）

```js
var str = "2020/13/01/01/03/05"
var array = str.split(/\//).map(Number)
var date = new Date(array[0], array[1] - 1, array[2], array[3], array[4], array[5])
Logger.log(date) // -> Fri Jan 01 01:03:05 GMT+09:00 2021
```

**……ほん…… ……とだ……**（来年の元日って金曜なんだ）

これだとうっかりタイポした時に**ちゃんと動作してくれないどころか全く予想外の結果になってしまいます**ね・・・。

結局**バリデーションを自分で設定しないといけない**となると、最初に挙げたメリットもおじゃんになってしまいました。無念。

#実装②のメリデメ
**メリット**：

 - 分割してフォーマットに流し込んでパースするだけ
 - パースした返り値がUNIX時の整数値になっているため目的に合致している
 - フォーマットが厳密に決まっているので、実装①のような厄介な挙動をしない

**デメリット**：

 - 特にない？

↓の記事では`Date.parse()`の返り値が`Date`ではなく整数になることを「**罠**」と形容していますが、今回のような使い方をする場合にはまさしく**割れ鍋に綴じ蓋**ですね。

[JavaScript の Date は罠が多すぎる - Qiita](https://qiita.com/labocho/items/5fbaa0491b67221419b4)

そして`"2020/13/01/01/03/05"`のようなありえない日付を指定した場合の挙動ですが、

```js
var str = "2020/01/13/12/34/56"
var array = str.split(/\//)
var date = Utilities.formatString("%s-%s-%sT%s:%s:%s+09:00", array[0], array[1], array[2], array[3], array[4], array[5])
date = Date.parse(date) / 1000 + ''
Logger.log(date) // -> NaN
```

**ちゃんと失敗してくれています**。これなら例外処理を入れるにも`if(isNaN(date))`で済みます。いいですね。っていうかマニュアルにちゃんと書いてあるんですけども。

>文字列を解釈できなかったり不正な日付 (例えば 2015-02-31) が指定された場合 NaN を返します。

[Date.parse() - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/parse)

ちなみに、`Date.parse()`といえば**ブラウザによって挙動が異なる**[^1]ことで有名（らしい）ですが、今回はGASなので**GASで動けば十分**ということで、これでいいと思います（GASの仕様が変わらなければ）。

#まとめ
2つの実装の長所・短所を比較した結果、実装②の方が良さそうだなと思いました。

書き始める前は実装①推しだったんですが、やはりマニュアルはちゃんと読んでおくべきですね。色んな意味で勉強になりました。

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

[^1]: でも→を見る限りでは、**最新バージョンなら**ほとんどのブラウザで問題なく動作するんですかね？[Date.parse() ブラウザー実装状況 - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/parse#Browser_compatibility)