## はじめに
先日、Elm v0.19 がリリースされました。公式ライブラリのリポジトリが
elm-lang から
elm に変更され、その中身も大きく再構成されています。
本記事では、これらの変更のうち特に時刻や日付の扱いに関する部分について、新しい API の使い方を含めて簡単に解説します。
## v0.18 における時刻の扱い
v0.18 では、時刻を扱う機能は標準パッケージ
elm-lang/core の中で提供されていました。時刻を扱う
Time モジュールと日付を扱う
Date モジュールで、それぞれデータ型や関数が定義されているのが特徴です。
なお、旧バージョンのライブラリは現在 Elm Packages の検索にはヒットしない ので、中身を確認するためには直接 URL にアクセスする必要があります。
### 旧 Time モジュール
時刻を扱う
Time 型を提供します。
Time 型の実体は
Float 型のエイリアスで、Unix Epoch からの経過ミリ秒数を表します。
メインの関数は現在時刻を取得する
now と指定した時間間隔で Msg を送出する
every です。その他、
every と組み合わせて使用する単位として
second : Time や
minute : Time が定義されており、例えば毎秒何かを行う Subscription は次のように書けます。
type Msg = DoSomethingAt Time.Time subscriptions : Model -> Sub Msg subscriptions _ = Time.every Time.second DoSomethingAt
### 旧 Date モジュール
日付を扱う
Date 型を提供します。また
Time 型との変換用の関数もこちらで定義されています。
その他、紛らわしいですが
Date モジュールでも
now 関数がエクスポートされており、こちらは現在の日付を取得します。
特徴的なのは、
Time 型と
Date 型の変換においてタイムゾーンを指定する機能がないことです。したがって API 定義上は
toTime : Date -> Time fromTime : Time -> Date
の変換は純粋な関数に見えますが、実際にはシステムのタイムゾーンに依存する副作用を持っていることになります。
## v0.19 における時刻の扱い
さて、今回新しくなったバージョンでは、旧
Time と
Date に相当する機能が再編成されてひとつのモジュール
Time になり、さらに別ライブラリ
elm/time として切り出されました。
### 新 Time モジュール
大きな変更点は、Unix 時間を表す
Posix 型に加えて、タイムゾーンを表す
Zone 型が陽に導入されたことです。
現在時刻を取得するには今まで通り
now で、現在のタイムゾーンを取得するには
here を使用します。例えば初期化の際、両者を同時に取得する
Cmd は以下のように書けます。
type Msg = SetSystemTime (Time.Zone, Time,Posix) setSystemTime : Cmd Msg setSystemTime = Task.perform SetSystemTime <| Task.map2 Tuple.pair Time.here Time.now
旧
Date モジュールにあった日付への変換も
Time の中にまとめられました。Unix 時間が同じでも実際の日付はタイムゾーンに依存するため、変換には
Zone 型が必要になっているのが分かります。新しい変換関数名には
toXXX で統一されており、
Day 型は
Weekday 型に、
dayOfWeek は
toWeekday に変更されました。
toYear : Zone -> Posix -> Int toMonth : Zone -> Posix -> Month toWeekday : Zone -> Posix -> Weekday toHour : Zone -> Posix -> Int toMinute : Zone -> Posix -> Int toSecond : Zone -> Posix -> Int toMillis : Zone -> Posix -> Int
また、旧
Time モジュールにあった時間の単位
minute や
second は外されました。ミリ秒単位で直接指定する必要があります。
### サンプル：デジタル時計
以上をまとめると、Elm v0.19 対応の簡単なデジタル時計は次のように実装することができます。
- 初期化時に
setSystemTimeで現在のタイムゾーンと時刻を取得
- それ以後 1000 ミリ秒ごとに
setCurrentTimeで現在時刻を更新
- 表示の際は
toHourと
toMinuteでタイムゾーン依存の時刻に変換
という流れになっています。
### Time.Extra モジュール
ところで、実際に時刻を扱うアプリを書いてみると、
elm/time はかなり非力であることがわかります。特に以下のようなケースは問題になりそうです。
- 時刻 + タイムゾーンから Unix 時間に変換できない（例：特定の日付と現在時刻を比較したい）
- 時刻の和や差が取れない（例：ちょうど 1 か月後の日付が欲しい）
- 時刻を丸めることができない（例：次に 00 秒になるタイミングで Msg を発生させたい）
このような問題を解決するために、
justinmimbs/time-extra が使用できます。
Time.Extra モジュールでは旧
Date 型に代わるものとして
Parts 型を定義しており、
Zone 型を組み合わせることで各タイムゾーンにおけるその時刻の Unix 時間を得ることができます。
Time モジュールの
second や代わる時間単位として
Interval 型を定義しており、これを使って「1 か月後の日付」や「次に 00 秒ちょうどになる時刻」が取得できるようになっています。
### まとめ
本記事では Elm v0.19 で刷新された
elm/time について、旧バージョンとの違いや使い方について簡単に解説しました。
- 時刻パッケージは
elm-lang/coreから
elm/timeに移動
- 原則
Posixを操作し、通常の時刻表示に変換した時は
Zoneと合わせる
- 時刻を操作するには
justinmimbs/time-extraライブラリが使える
ところで、今回紹介した新しい API を使ってちょっと面白いサンプルアプリを作ってみたので、GIF アニメにしたものを貼っておきます。
元ネタは Humans since 1982 の作品 ClockClock 24 です。ソースコードは以下にあるので
elm/time の使い方の参考に。
ちなみにこのサンプル、実際には
elm/time ではなくそれ以外の部分の実装のほうが大変だったのですが、それはまた別の話。