指定時刻のエポック秒を得る方法（moment.jsとshell/gdate）

## 目的

- 時刻や日時の計算は一度エポック秒に変換して加算、減算するのがもっとも間違いが少ないです。

- そのためJavaScriptでも指定時刻からエポック秒を得たいことがJavaScriptでもShell(Bash)でもあります。サマータイムとか閏とか手動で設定するのは悪夢です。オリジナルコマンドなんか作るのはやめた方がいいです（この考えに行き着くまで苦い経験を繰り返しました）。

- JavaScriptのデフォルトのDateで格闘するのも否定しませんが、時間かけずにさっさとコーディングしたいものです。

- JavaScriptには`moment.js`を導入することにしています。cgiではbash/gdateを使っています

- `moment.js`と`gdate`は互換の無い記述なのでその備忘録としての書いておきます。

- 検索性が悪かったので全面書き換えしました[2020-09-06]

  

## 環境

- macOS
- moment.js 2.17
- bash 5.0.17
- date (GNU coreutils) 8.32

## リンク

- [UNIX時間](https://ja.wikipedia.org/wiki/UNIX%E6%99%82%E9%96%93)
- [Moment.js Documentation](https://momentjs.com/docs/#/parsing/)
- [日付からエポック、エポックから日付をワンライナーで](https://tech.buty4649.net/entry/2014/03/20/%E6%97%A5%E4%BB%98%E3%81%8B%E3%82%89%E3%82%A8%E3%83%9D%E3%83%83%E3%82%AF%E3%80%81%E3%82%A8%E3%83%9D%E3%83%83%E3%82%AF%E3%81%8B%E3%82%89%E6%97%A5%E4%BB%98%E3%82%92%E3%83%AF%E3%83%B3%E3%83%A9%E3%82%A4)
- [シェルスクリプトでunixtimeへ変換・unixtimeから変換をしてみる](https://genzouw.com/entry/2019/05/16/082057/1499/)
- [シェルスクリプトでUNIXTIMEの取得と日時との相互変換（POSIX準拠dateコマンドのみ使用）](https://qiita.com/ko1nksm/items/e61728f863c1b2bc829d)

###  JavaScriptでエポック秒と指定フォーマット日時の相互変換

#### moment.jsの準備

- JavaScriptのmoment.jsライブラリを使います。`<script src="..."></script>`なんかでCDNで読めばいいと思います。
- moment.jsはデフォルトではローカル時間で解析および表示されます。

```{JavaScript}
<!-- moment.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/locale/ja.js"></script>
```



## JavaScript(moment.js)で現在時間と現在のエポック秒の取得

```{JavaScript}
// 現在の時間の取得（ここで出力は省略）
> moment()
// 現在のエポック秒の取得
moment.unix()
< 1595722136
```


#### 時刻→エポック秒（moment.js）

```{JavaScript}
// 第１引数で指定した時刻のフォーマットは第２引数で指定します。
> moment("200101 123456", "YYMMSS HHmmss").unix()
< 1577849696
```

#### エポック秒→時刻（ moment.js）

```{JavaScript}
// エポック秒から指定フォーマットに戻します。
> moment.unix(1577849696).format("YYMMDD HHmmss")
< "200101 123456"

// もちろん可読性の高いフォーマットにできます。
> moment.unix(1577849696).format("YYYY-MM-DD HH:mm:ss Z")
< "2020-01-01 12:34:56 +09:00"
```

#### moment.jsの時刻のフォーマット（使うものだけ）

- moment()の時刻表現は非常にフレキシブルなのですが、shellの`gdate`と表現は似ていて違います。なので忘れます。
- 私は使うのは下記に限っています。

<strong> 表1 moment.jsのフォーマット（[ここのリンク](https://momentjs.com/docs/#/parsing/string-format/)から改変）</strong>

| Input | Example | Description                |
| :---- | :------ | :------------------------- |
| YY    | 14      | 2 digit year               |
| MM    | 1..12   | Month number               |
| HH    | 0..23   | Hours (24 hour time)       |
| mm    | 0..59   | Minutes                    |
| ss    | 0..59   | Seconds                    |
| Z     | +12:00  | Offset from UTC as +-HH:mm |
| ZZ    | +0900   | Offset from UTC as +-HHmm  |

- 使用例；
  - `moment("200101 123456", "YYMMSS HHmmss").unix()`
  - `moment.unix(1577849696).format("YYMMDD HHmmss")`

## エポック秒⇄時刻の相互変換(bash)

- cgi側でも同じことをしたいですね。
- 実は`bash`の方が面倒です。

[Bashで日時文字列をエポックに変換する](https://www.it-swarm.dev/ja/bash/bash%E3%81%A7%E6%97%A5%E6%99%82%E6%96%87%E5%AD%97%E5%88%97%E3%82%92%E3%82%A8%E3%83%9D%E3%83%83%E3%82%AF%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B/1066628489/)

#### gdateを使う（インストール）

- GNU `date`を使いたいのでbashの`coreutil`をインストールしておきます。詳しくは下記が面白いです。使い方も載っているサイトあります。

- [RHL7/CentOS7の時代なのでcoreutils 8.22まで追いかけてみた](https://qiita.com/ma2saka/items/4099443efc2b93fe8fcd)

#### 時刻（UNIX形式）→エポック秒(bash)

```{bash}
gdate -d "2020-01-01 12:34:56" +%s
1577849696
# こちらでもOK
gdate -d '2020/01/01 12:34:56' +%s
1577849696
```

- エポック秒を得る定番ですね。
- `gdate`は入力フォーマットと出力フォーマットを別個に指定できないのでUNIX時間フォーマットで指定する必要があります。
- 時間指定にはUNIX形式の指定が必要ですが柔軟性を持たせたいですね。


#### 時刻→エポック秒（awkのmktimeを使う)

```{bash}
echo "200101 123456" | fold  -s -w2 |tr '\n' ' ' |awk '{print mktime(20$0)}'
1577849696
# こっちが簡単かな
echo "200101 123456" | fold  -s -w2 |xargs|awk '{print mktime(20$0)}'
1577849696 
```

- 少しだけ解説：
- 文字列200101 123456を`fold -s -w2`でスペースを気にしないで二文字に分けます。
- それを`tr '\n' ' '`で改行をスペースに分けて、awkの`mktime()`でエポック秒に変換します。`mktime()`で必要な最初の項目はは４桁年なので20を$0に加えています。
- 最初、awkで200101を2020-01-01とかに変換すればいいかと考えましたがawkにはmktime()があるからそれ使えばいいじゃんと考え直した次第です。

#### 時刻→エポック秒（utconvを使う）

- こんなコマンドもあります。TZの設定もできます（詳しくはリンク先を）
- [rich mikanさん製コマンド：utconv](https://gist.github.com/richmikan/8703117)

```{bash}
cd /usr/local/bin
# インストールします
wget https://gist.githubusercontent.com/richmikan/8703117/raw/e44e6a357cb7423c1a1e58df96da9792c43e80aa/utconv
# 実行権を与えてください
chmod +x utconv
echo "20200101123456" | utconv
1577849696
```

- この文字列生成はshellでは比較的簡単でしょう。


#### エポック秒→時刻（gdate）

- gdateではエポック秒は`@数字`で指定します。

```{bash}
gdate '-d @1577849696'
水  1  1 12:34:56 JST 2020
# 可読性の高いフォーマットに
gdate '-d @1577849696' +"%Y-%m-%d %H:%M:%S (%Z)%:z" 
2020-01-01 12:34:56 (JST)+09:00
```

指定したのは2020年01月01日12:34:56でしたので、正しく戻っています。ローカル時間帯の(JST)で戻っていますね。



### <strong>表2 GNU dateのフォーマット（[ここのリンク](https://man7.org/linux/man-pages/man1/date.1.html)から改変）</strong>

| Input | Example      | Description              |
| :---- | :----------- | :----------------------- |
| %y    | 00..99       | last two digits of year  |
| %m    | 01..12       | month                    |
| %d    | e.g., 01     | day of month             |
| %H    | 00..23       | hour                     |
| %M    | 00..59       | minute                   |
| %S    | 00..60       | second                   |
| %z    | e.g., -0400  | +hhmm numeric time zone  |
| %:z   | e.g., -04:00 | +hh:mm numeric time zone |

- 使用例
  - `gdate '+%y%m%d %H%M%S'`で200726 095643を得ます

#### エポック秒→時刻(convert -rを使う）

- 逆の計算もできます。ただしYYMMDDHHmmssです。これから`sed/awk`などで色々なフォーマットにできますね。

```{bash}
echo "1577849696" | utconv -r
20200101123456
```

#### エポック秒→時刻(awkを使う)
```bash
echo "1577849696" | awk '{print strftime("%Y-%m-%d,%H:%M:%S",$0)}'
2020-01-01,12:34:56
```


- 自分の備忘録のために書きましたがどなたかのお役に立てれば嬉しいです。