时间
现在我们要制作一个数字时钟。(模拟时钟将是练习!)
到目前为止,我们专注于命令。利用 HTTP 和随机示例,我们命令 Elm 立即执行特定工作,但对于时钟来说,这是一种奇怪的模式。我们始终希望知道当前时间。这就是订阅派上用场的地方!
首先,单击蓝色的“编辑”按钮,在在线编辑器中大致浏览一下代码。
import Browser
import Html exposing (..)
import Task
import Time
-- MAIN
main =
Browser.element
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
-- MODEL
type alias Model =
{ zone : Time.Zone
, time : Time.Posix
}
init : () -> (Model, Cmd Msg)
init _ =
( Model Time.utc (Time.millisToPosix 0)
, Task.perform AdjustTimeZone Time.here
)
-- UPDATE
type Msg
= Tick Time.Posix
| AdjustTimeZone Time.Zone
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Tick newTime ->
( { model | time = newTime }
, Cmd.none
)
AdjustTimeZone newZone ->
( { model | zone = newZone }
, Cmd.none
)
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Time.every 1000 Tick
-- VIEW
view : Model -> Html Msg
view model =
let
hour = String.fromInt (Time.toHour model.zone model.time)
minute = String.fromInt (Time.toMinute model.zone model.time)
second = String.fromInt (Time.toSecond model.zone model.time)
in
h1 [] [ text (hour ++ ":" ++ minute ++ ":" ++ second) ]
新增内容均来自 elm/time
包。我们来看看这些部分!
Time.Posix
和 Time.Zone
要在编程中成功处理时间,我们需要三个不同的概念
人类时间 — 这是你时钟上(上午 8 点)或日历上(5 月 3 日)看到的内容。很好!但如果我下午 8 点打电话给波士顿的朋友,那么加拿大温哥华的朋友是什么时间?如果东京时上午 8 点,那么纽约难道不是同一天吗?(否!)因此,在以不断变化的政治边界和 夏令时 不一致的使用为基础的 时区 之间,基本上绝不应将人类时间存储在你的
Model
或数据库中!它仅供显示!POSIX 时间 — 使用 POSIX 时间,你的居住地点或一年中的时间并不重要。它只是自某个任意时刻(1970 年)以来经过的秒数。无论你在地球上的哪个地方,POSIX 时间都是一样的。
时区 — “时区”是一组数据,使你能够将 POSIX 时间转换为人类时间。这不仅仅是
UTC-7
或UTC+3
!时区比一个简单的偏移量复杂得多!每次 佛罗里达州永久切换至夏令时 或 萨摩亚从 UTC-11 切换至 UTC+13 时,某个可怜的家伙就会在 IANA 时区数据库 中添加注释。该数据库已加载到每台计算机上,在 POSIX 时间和数据库中的所有特殊情况下,我们可以确定人类时间!
因此,若向人显示时间,你必须始终知道 Time.Posix
和 Time.Zone
。就这样!所以所有“人类时间”内容都是针对 视图
函数,而不是 模型
。事实上,你可以在我们 视图
中看到它
view : Model -> Html Msg
view model =
let
hour = String.fromInt (Time.toHour model.zone model.time)
minute = String.fromInt (Time.toMinute model.zone model.time)
second = String.fromInt (Time.toSecond model.zone model.time)
in
h1 [] [ text (hour ++ ":" ++ minute ++ ":" ++ second) ]
Time.toHour
函数采用 Time.Zone
和 Time.Posix
为我们返回一个来自 0
到 23
的 Int
,表示你的时区中 currently is 的时间。
elm/time
的自述文件中提供了更多有关在时间处理中的信息的Definitely 在使用更多时间之前阅读它!尤其是在使用计划、日历等功能时。
订阅
好的,我们该如何获取我们的 Time.Posix
呢?使用订阅!
subscriptions : Model -> Sub Msg
subscriptions model =
Time.every 1000 Tick
我们使用 Time.every
函数
every : Float -> (Time.Posix -> msg) -> Sub msg
它采用两个参数
- 以毫秒为单位的时间间隔。我们说
1000
,表示每秒。但我们也可以说每分钟60 * 1000
或每五分钟5 * 60 * 1000
。 - 一个将当前时间转换成
Msg
的函数。因此,每秒,当前时间都将转换为Tick <time>
,用于我们的更新
函数。
这是任何订阅的基本模式。你提供一些配置,并描述如何生成 Msg
值。还不错!
任务执行
获取 Time.Zone
有点棘手。我们的程序创建了一个命令,其中包含
Task.perform AdjustTimeZone Time.here
通读 Task
文档是理解该行的最佳方式。这些文档旨在实际解释新概念,我认为在这里包含该信息的较差版本会产生很多干扰。关键在于我们命令运行时无论代码在哪里运行,都要向我们提供 Time.Zone
。
练习