GASで引数に日時を取るスラッシュコマンドを作るにあたって、「スラッシュコマンドで指定する日付と時刻はわかりやすいYYYY/MM/DD/hh/mm/ssがいい。でもSlack APIを叩く時はUNIXタイムスタンプで指定しないといけない」という都合により、GASで「YYYY/MM/DD/hh/mm/ss→UNIXタイムスタンプ」の変換をすることになったんですが、思いの外ネット上にベストマッチなソリューションが見当たらなかったので書き残しておきます。
#実装①
//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
#実装②
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
#1000で割る意味
getTime()
やDate.parse()
によって返ってくるUNIX時は単位がミリ秒になっています。Slack APIで指定するタイムスタンプは秒単位でなければならないので、1000で割った値を文字列に変換しています。
#実装①のメリデメ
メリット:
- 一度数値に変換するので、引数のバリデーションとかゼロ埋めとか気にしなくていい(?)
- 短くて読みやすい(?)
デメリット:
-
Date
オブジェクトを経由するのが煩わしくないこともない -
Date
クラスのメソッドの仕様が厄介
デメリットの方について少し説明しますと、数列からDateオブジェクトを生成するnew Date()
の構文は
new Date(year, monthIndex [, day [, hours [, minutes [, seconds [, milliseconds]]]]]);
なのですが、このmonthIndex
は0から11で指定するんですね。
注: 引数 monthIndex は 0 から始まります。 つまり 1 月 = 0、 12 月 = 11 です。
なので、new Date()
に渡す時にそこだけ- 1
する必要があります。
あと、公式マニュアルを見返していて気付いたんですが(強調は筆者)、
注: Date を複数の引数を伴ってコンストラクタとして呼び出された場所では、値が論理範囲より大きくても (month 値に 13 を与えたり、minute 値に 70 を与える)、調整された値になります。つまり、new Date(2013, 13, 1) は、new Date(2014, 1, 1) と等しくなるように調整され、両者とも 2014-02-01 の日付を生成します
……何…… ……だと……(画像略)
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
そして"2020/13/01/01/03/05"
のようなありえない日付を指定した場合の挙動ですが、
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
ちなみに、Date.parse()
といえばブラウザによって挙動が異なる1ことで有名(らしい)ですが、今回はGASなのでGASで動けば十分ということで、これでいいと思います(GASの仕様が変わらなければ)。
#まとめ
2つの実装の長所・短所を比較した結果、実装②の方が良さそうだなと思いました。
書き始める前は実装①推しだったんですが、やはりマニュアルはちゃんと読んでおくべきですね。色んな意味で勉強になりました。
最後まで読んでいただきありがとうございました。
-
でも→を見る限りでは、最新バージョンならほとんどのブラウザで問題なく動作するんですかね?Date.parse() ブラウザー実装状況 - JavaScript | MDN ↩