Hasta ahora nos hemos centrado en los comandos. Con los ejemplos de HTTP y aleatoriedad, le ordenamos a Elm que realizara una tarea específica inmediatamente, pero ese es un patrón un tanto extraño para un reloj. Siempre queremos saber la hora actual. ¡Aquí es donde entran en juego las suscripciones!
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) ]
Todo lo nuevo proviene del paquete elm/time.
Para trabajar con el tiempo correctamente en programación, necesitamos tres conceptos diferentes:
Hora Humana: Es lo que se ve en los relojes (8 a. m.) o en los calendarios (3 de mayo). ¡Genial! Pero si mi llamada es a las 8 a. m. en Boston, ¿qué hora es para mi amigo en Vancouver? Si es a las 8 a. m. en Tokio, ¿es el mismo día en Nueva York? (¡No!) Por lo tanto, entre las zonas horarias basadas en fronteras políticas en constante cambio y el uso inconsistente del horario de verano, ¡la hora humana básicamente nunca debería almacenarse en el modelo ni en la base de datos! ¡Es solo para visualización!
POSIX Time: Con POSIX Time, no importa dónde vivas ni en qué época del año sea. Es simplemente la cantidad de segundos transcurridos desde un momento arbitrario (en 1970). Donde quiera que vayas en la Tierra, la hora POSIX es la misma.
Zonas Horarias: Una "zona horaria" es un conjunto de datos que te permite convertir la hora POSIX en hora humana. ¡Pero no se trata solo de UTC-7 o UTC+3! Las zonas horarias son mucho más complicadas que una simple diferencia horaria. Cada vez que Florida cambia al horario de verano para siempre o Samoa cambia de UTC-11 a UTC+13, alguien añade una nota a la base de datos de zonas horarias de la IANA. Esa base de datos se carga en cada ordenador, y entre la hora POSIX y todos los casos excepcionales de la base de datos, ¡podemos calcular la hora humana!
Así que, para mostrarle la hora a un ser humano, siempre debes conocer Time.Posix y Time.Zone. ¡Eso es todo! Así que todo lo relacionado con la "hora humana" es para la función de vista, no para el modelo. De hecho, puedes verlo en nuestra vista:
vista: Modelo -> Mensaje HTML
vista modelo =
let
hora = String.fromInt (Time.toHour modelo.zone modelo.time)
minuto = String.fromInt (Time.toMinute modelo.zone modelo.time)
segundo = String.fromInt (Time.toSecond modelo.zone modelo.time)
in
h1 [] [texto (hora ++ ":" ++ minuto ++ ":" ++ segundo) ]
La función Time.toHour toma Time.Zone y Time.Posix nos devuelve un entero de 0 a 23 que indica qué hora es en tu zona horaria.
¿Cómo obtenemos nuestro Time.Posix? ¡Con una suscripción! Suscripciones: Modelo -> Submensaje
subscriptions : Model -> Sub Msg
subscriptions model =
Time.every 1000 Tick
Usamos la función Time.every:
every : Float -> (Time.Posix -> msg) -> Sub msg
Requiere dos argumentos:
- Un intervalo de tiempo en milisegundos. Dijimos 1000, que significa cada segundo. Pero también podríamos decir 60 * 1000 por cada minuto, o 5 * 60 * 1000 por cada cinco minutos.
- Una función que convierte la hora actual en un mensaje. Por lo tanto, cada segundo, la hora actual se convertirá en un Tick <time> para nuestra función de actualización.
Ese es el patrón básico de cualquier suscripción. Se proporciona cierta configuración y se describe cómo generar valores de mensaje.
Obtener Time.Zone es un poco más complicado. Nuestro programa creó un comando con:
Task.perform AdjustTimeZone Time.here
Leer la documentación de la tarea es la mejor manera de comprender esta línea. La documentación está escrita para explicar los nuevos conceptos, y creo que sería una digresión excesiva incluir una versión peor de esa información aquí. La cuestión es simplemente que ordenamos al entorno de ejecución que nos proporcione la Time.Zone dondequiera que se ejecute el código.
