MixJuiceで現在の日時を取得する

# 何をするか？

[MixJuice](http://mixjuice.shizentai.jp/)経由でWeb APIを利用し、[IchigoJam](https://ichigojam.net/)から現在の日時を取得します。

# 現在の日時を取得するWeb API

現在の日時を取得するWeb APIを探したところ、以下のものが見つかりました。

## 日本標準時プロジェクト

[日本標準時プロジェクト　https/http を介してアクセスされる場合](https://www.nict.go.jp/JST/http.html)

現在時刻を時分秒形式で返すAPIが定義されています。

```
https://ntp-a1.nict.go.jp/cgi-bin/time
```

以下のような形式で現在時刻が取得できる仕様です。

```
Sat Jun  5 03:53:33 2021 JST
```

月が数字ではなく英字列なのが気になりますが、余計なデータが無く扱いやすそうな仕様！

さらに、ヘッダは`Access-Control-Allow-Origin:*`を含み、HTTPSにも対応する仕様となっています。
IchigoJam webのバーチャルMixJuice対応もバッチリな仕様！

…しかし、毎月6日～月末はサービス停止、
2022年3月31日をもって完全にサービス終了となっており、使い物になりません。
入出力の仕様が素晴らしいだけに、非常に残念！！

## IchigoJam + MixJuice用 日付＆時間データ

[IchigoJam + MixJuice用 日付＆時間データ](http://www.openspc2.org/data/date/)

以下のURLで日本の現在時刻を取得できます。

```
http://www.openspc2.org/data/date/full.txt
```

以下のような形式の結果が返ります。

```
2021
12
14
2
0
23
```

秒の情報は取得できないようですが、「MixJuice用」と銘打っているだけあって形式のシンプルさは最高です。
しかし、ヘッダに`Access-Control-Allow-Origin`が含まれておらず、
HTTPSにも対応していない(接続できない)ので、IchigoJam webのバーチャルMixJuiceからのアクセスは難しそうです。

## World Time API

[World Time API: Simple JSON/plain-text API to obtain the current time in, and related data about, a timezone.](https://worldtimeapi.org/)

以下のURLで日本の現在時刻を取得できます。

```
https://worldtimeapi.org/api/timezone/Asia/Tokyo
```

また、アクセス元のIPアドレスに基づくタイムゾーンの自動選択もできます。

```
https://worldtimeapi.org/api/ip
```

以下のような形式の結果が返ります。

```
{"abbreviation":"JST","client_ip":"35.199.178.127","datetime":"2021-12-14T00:12:43.579568+09:00","day_of_week":2,"day_of_year":348,"dst":false,"dst_from":null,"dst_offset":0,"dst_until":null,"raw_offset":32400,"timezone":"Asia/Tokyo","unixtime":1639408363,"utc_datetime":"2021-12-13T15:12:43.579568+00:00","utc_offset":"+09:00","week_number":50}
```

見やすく整形すると、以下のようになります。

```json
{
	"abbreviation": "JST",
	"client_ip": "35.199.178.127",
	"datetime": "2021-12-14T00:12:43.579568+09:00",
	"day_of_week": 2,
	"day_of_year": 348,
	"dst": false,
	"dst_from": null,
	"dst_offset": 0,
	"dst_until": null,
	"raw_offset": 32400,
	"timezone": "Asia/Tokyo",
	"unixtime": 1639408363,
	"utc_datetime": "2021-12-13T15:12:43.579568+00:00",
	"utc_offset": "+09:00",
	"week_number": 50
}
```

HTTPSでのアクセスに対応しており、ヘッダに`access-control-allow-origin: *`が含まれるため、
IchigoJam webのバーチャルMixJuiceからのアクセスもしやすいです。

ただし、単純に時刻を取得するためだけに使うには結果が長いです。
結果をJSONではなくプレーンテキストで返すモードもありますが、結果の長さはあまり変わりませんでした。

## TimeAPI

[Simple and Free Time API | TimeAPI](https://www.timeapi.io/)

以下のURLで日本の現在時刻を取得できます。

```
https://www.timeapi.io/api/Time/current/zone?timeZone=Asia/Tokyo
```

以下のような形式の結果が返ります。

```
{"year":2021,"month":12,"day":13,"hour":23,"minute":51,"seconds":12,"milliSeconds":388,"dateTime":"2021-12-13T23:51:12.3888497","date":"12/13/2021","time":"23:51","timeZone":"Asia/Tokyo","dayOfWeek":"Monday","dstActive":false}
```

見やすく整形すると、以下のようになります。

```json
{
	"year": 2021,
	"month": 12,
	"day": 13,
	"hour": 23,
	"minute": 51,
	"seconds": 12,
	"milliSeconds": 388,
	"dateTime": "2021-12-13T23:51:12.3888497",
	"date": "12/13/2021",
	"time": "23:51",
	"timeZone": "Asia/Tokyo",
	"dayOfWeek": "Monday",
	"dstActive": false
}
```

HTTPSには対応していますが、`Access-Control-Allow-Origin`ヘッダは入っておらず、
IchigoJam webのバーチャルMixJuiceでの利用は若干面倒かもしれません。
([CORS Everywhere](https://addons.mozilla.org/ja/firefox/addon/cors-everywhere/)などの拡張機能を用いればアクセスは可能です)

## World Clock API

[Home Page](http://worldclockapi.com/)

以下のURLでUTCの現在時刻(日本時間の9時間前)が取得できます。

```
http://worldclockapi.com/api/json/utc/now
```

以下のような形式の結果が返ります。

```
{"$id":"1","currentDateTime":"2021-12-13T15:30Z","utcOffset":"00:00:00","isDayLightSavingsTime":false,"dayOfTheWeek":"Monday","timeZoneName":"UTC","currentFileTime":132838830284824919,"ordinalDate":"2021-347","serviceResponse":null}
```

見やすく整形すると、以下のようになります。

```json
{
	"$id": "1",
	"currentDateTime": "2021-12-13T15:30Z",
	"utcOffset": "00:00:00",
	"isDayLightSavingsTime": false,
	"dayOfTheWeek": "Monday",
	"timeZoneName": "UTC",
	"currentFileTime": 132838830284824910,
	"ordinalDate": "2021-347",
	"serviceResponse": null
}
```

ただし、日本時間の直接取得には対応していないようです。
(`utc`の代わりに`jst`や[すべてのタイムゾーンでの現在の時間](https://24timezones.com/jo_sekai3.php)に載っている他のUTC+9のタイムゾーンの名前を入れると、無効なタイムゾーン(not a valid Time Zone)としてエラーになりました)
また、秒単位の時刻の取得も簡単にはできないようです。
([CyberChefを用いる](https://gchq.github.io/CyberChef/#recipe=Windows_Filetime_to_UNIX_Timestamp%28%27Seconds%20%28s%29%27,%27Decimal%27%29From_UNIX_Timestamp%28%27Seconds%20%28s%29%27%29&input=MTMyODM4ODMwMjg0ODI0OTEw)と`currentFileTime`から秒単位の時刻を取り出せるので、不可能ではないと思われます)

さらに、HTTPSへの対応が不完全(接続は出来るが証明書の名前が無効)であり、
ヘッダに`Access-Control-Allow-Origin`が含まれないため、
IchigoJam webのバーチャルMixJuiceからのアクセスは難しそうです。

## まとめ

|API|HTTPS対応|CORS対応|結果のシンプルさ|日本時間対応|秒単位対応|時間の制約無くアクセス可能|
|---|:-:|:-:|:-:|:-:|:-:|:-:|
|[日本標準時プロジェクト](https://www.nict.go.jp/JST/http.html)|○|○|○|○|○|×|
|[IchigoJam + MixJuice用 日付＆時間データ](http://www.openspc2.org/data/date/)|×|×|○|○|×|○|
|[World Time API](https://worldtimeapi.org/)|○|○|×|○|○|○|
|[TimeAPI](https://www.timeapi.io/)|○|×|×|○|○|○|
|[World Clock API](http://worldclockapi.com/)|×|×|×|×|△|○|

(ヘッダが`Access-Control-Allow-Origin:*`を含むことを「CORS対応」としています)

# 現在の日時を取得する

## 方針

今回は、IchigoJam webのバーチャルMixJuiceからのアクセスがしやすく、
利用時間の制限も無い [World Time API](https://worldtimeapi.org/) を用いて現在の日時を取得することにしました。
取得する形式は、データの終わりが`}`で明示されるためわかりやすいJSONを用いることにしました。

返されるデータのうち、`"datetime":"2021-12-14T00:12:43.579568+09:00",`の部分に日時の情報が、
`"day_of_week":2,`の部分に曜日の情報が入っているので、これらから情報を抜き出します。

今回扱うJSONのデータでは、オブジェクトのネストが無く、
データの文字列にカンマやエスケープ、閉じカッコは含まれていないようです。
そこで、この性質を利用し、以下のようなロジックで情報を抜き出します。

* `,`があったら、名前モードにする。
* `:`があったら、データモードにする。
* `"`があったら、文字列モードかどうかを切り替える。(名前/データモードとは別に管理するモード)
* `}`があったら、読み取りを終了する。
* 名前モードの文字列モードでは、名前の識別のため、名前の文字の文字コードの和を計算する。
* データモードでは、十進数値を読み取り、数字の後数字以外が来たら読み取った数値を記録する。
  * `"datetime"`の後では、日時を表す数値をそれぞれ配列に格納する。
  * `"day_of_week"`の後では、曜日を表す数値を変数に格納する。
  * それ以外では、無視する。

## 実装

### コード全体

```
10 ' ｼﾞｺｸ ｼｭﾄｸ
20 ?"MJ GETS worldtimeapi.org/api/timezone/Asia/Tokyo"
30 W=0:LET[0],0,0,0,0,0,0:N=1:H=0:S=0:P=0:V=0:E=0
40 C=INKEY():IF C=0 GOTO 40 ELSE GOTO 70
50 CLT
60 C=INKEY():IF TICK()>=30 GOTO 160 ELSE IF C=0 GOTO 60
70 IF C=#22 S=!S:IF S AND N H=-#22
80 IF S AND N H=H+C
90 IF C=#3A:N=0
100 IF C=#2C:N=1
110 IF #30 <= C AND C <= #39 V=V*10+C-#30:E=1 ELSE IF E GOTO 130
120 IF C=#7D GOTO 160 ELSE GOTO 50
130 IF H=845 AND P<6 [P]=V:P=P+1
140 IF H=1149 W=V
150 V=0:E=0:GOTO 120
160 ?[0];"/";[1];"/";[2];" (";
170 IF W=0 ?"Sun";
180 IF W=1 ?"Mon";
190 IF W=2 ?"Tue";
200 IF W=3 ?"Wed";
210 IF W=4 ?"Thu";
220 IF W=5 ?"Fri";
230 IF W=6 ?"Sat";
240 ?") ";[3];":";[4];":";[5]
```

### 解説

タイトルを置きます。

```
10 ' ｼﾞｺｸ ｼｭﾄｸ
```

クエリを送ります。

```
20 ?"MJ GETS worldtimeapi.org/api/timezone/Asia/Tokyo"
```

以下の変数を初期化します。

* `W`：曜日
* 配列：日時
* `N`：名前モードか
* `H`：名前の文字の文字コードの和
* `S`：文字列モードか
* `P`：次に読み取った数値を格納する配列の要素
* `V`：読み取り中の数値
* `E`：数値を読み取り中か

```
30 W=0:LET[0],0,0,0,0,0,0:N=1:H=0:S=0:P=0:V=0:E=0
```

結果の最初の文字を時間無制限で待ち、読み取ります。

```
40 C=INKEY():IF C=0 GOTO 40 ELSE GOTO 70
```

結果の2文字目以降を読み取ります。0.5秒文字が来なかったら、読み取りを打ち切ります。

```
50 CLT
60 C=INKEY():IF TICK()>=30 GOTO 160 ELSE IF C=0 GOTO 60
```

読み取った文字が`"`なら、文字列モードかどうかを切り替えます。
名前モードで文字列モードに入るなら、名前の文字の文字コードの和`H`を初期化します。
(この`"`の文字コードも足されるため、その分マイナスにしておきます)

```
70 IF C=#22 S=!S:IF S AND N H=-#22
```

名前モードの文字列モードなら、今の文字の文字コードを足します。

```
80 IF S AND N H=H+C
```

読み取った文字が`:`であれば、データモードに切り替えます。

```
90 IF C=#3A:N=0
```

読み取った文字が`,`であれば、名前モードに切り替えます。

```
100 IF C=#2C:N=1
```

読み取った文字が数字であれば、数値の読み取り状態を更新します。
数値を読み取った状態で数字以外の文字を読み取った時は、読み取った数値を記録する処理に行きます。

```
110 IF #30 <= C AND C <= #39 V=V*10+C-#30:E=1 ELSE IF E GOTO 130
```

読み取った文字が`}`であれば、読み取りを終了します。
そうでなければ、次の文字の読み取りに行きます。

```
120 IF C=#7D GOTO 160 ELSE GOTO 50
```

読み取った名前が`"datetime"`であれば、読み取った日時を表す数値を配列に格納します。

```
130 IF H=845 AND P<6 [P]=V:P=P+1
```

読み取った名前が`"day_of_week"`であれば、読み取った数値を曜日を表す変数に格納します。

```
140 IF H=1149 W=V
```

数値の読み取り状態を初期化し、次の文字の読み取りに行きます。

```
150 V=0:E=0:GOTO 120
```

読み取った日時と曜日を出力します。

```
160 ?[0];"/";[1];"/";[2];" (";
170 IF W=0 ?"Sun";
180 IF W=1 ?"Mon";
190 IF W=2 ?"Tue";
200 IF W=3 ?"Wed";
210 IF W=4 ?"Thu";
220 IF W=5 ?"Fri";
230 IF W=6 ?"Sat";
240 ?") ";[3];":";[4];":";[5]
```

## 実験結果

MixJuiceを接続した状態でプログラムを実行し、しばらく待つと、

```
2021/12/14 (Tue) 3:41:5
```

のような形式で現在の日時が出力されました。
(取得を開始したタイミングの日時なので、出力されるタイミングからは遅れています)

MixJuice実機では、`MJ SPW (送信間隔[ms])`コマンドでデータをIchigoJamに転送する速度を調整できるようです。
また、今回のプログラムでは、

```
75 ?CHR$(C);
```

という行を追加することで、受信したデータを出力して確認できるようになります。

IchigoJam webでは日時の取得に成功し、実行を開始してから約50秒後に日時が出力されました。
(現在の)バーチャルMixJuiceでは`MJ SPW`コマンドは効かないようで、データは欠けていました。

IchigoJam実機 (1.0.0) では、送信間隔を40msに設定すると、データが欠けて曜日の取得に失敗しました。
送信間隔を45msに設定すると、データが欠けずに日時の取得に成功し、
実行を開始してから約16秒で日時が出力されました。

IchigoJam実機 (1.3.1) では、送信間隔を15msに設定してもデータは欠けず、
実行を開始してから約7秒で日時が出力されました。
また、送信間隔を0msに設定しても最初の128バイト程度は受信できるようで、
データは欠けたものの日時の取得には成功し、実行を開始してから約3秒で日時が出力されました。

# おわりに

Web APIを利用してIchigoJamからMixJuice経由で現在の日時を取得し、出力することができました。

ちなみに、執筆時において[MixJuiceのページ](http://mixjuice.shizentai.jp/)もHTTPSには対応していないようです。
(接続はできるが証明書の名前が無効で、無視して続行すると404 Not Foundになりました)

IchigoJamはjig.jpの登録商標です。