21

I'm running elm-repl to play around with the language.

I'd like to see what the current time is. How would I do that? It doesn't appear to be possible with the current library. Why is that?


EDIT: I made a package to help with this. http://package.elm-lang.org/packages/z5h/time-app

This was asked around elm 0.15 - things are different in elm 0.17 & 0.18: see How do I get the current time in Elm 0.17/0.18?

glennsl
  • 28,186
  • 12
  • 57
  • 75
Mark Bolusmjak
  • 23,606
  • 10
  • 74
  • 129
  • A couple of working examples: http://stackoverflow.com/a/35120279/480608 (using Elm v0.16.0). – Raine Revere Jan 31 '16 at 22:26
  • Possible duplicate of [How do I get the current time in Elm 0.17/0.18?](http://stackoverflow.com/questions/38021777/how-do-i-get-the-current-time-in-elm-0-17-0-18) – clozach Jan 27 '17 at 08:07

8 Answers8

19

Update for 0.19 It is not possible to get the current time using the standard library.. You need to use elm/time. As with 0.18, all you need is a command and Msg to handle the result

type Msg
    = OnTime Time.Posix 

getTime : Cmd Msg
getTime = 
    Task.perform OnTime Time.now 

Update for 0.18 This has got simpler again. Now all you need is a command and Msg to handle the result

type Msg
    = OnTime Time 

getTime : Cmd Msg
getTime = 
    Task.perform OnTime Time.now 

See this Ellie

Original answer

With 0.17, this got a whole lot easier. There is now a Task in the Time library. So for example, we now have:

Time.now
|> Task.Perform NoOp CurrentTime
Simon H
  • 20,332
  • 14
  • 71
  • 128
  • 1
    For everyone's benefit, I've asked the question again (for 0.17) here http://stackoverflow.com/q/38021777/131227. The way I see it, is that it's easy to get the time as you show. But it's much tricker (impossible) to get the time that an arbitrary message occurred. Many apps have this type of bookkeeping requirement where a "created_at" or "updated_at" values are maintained. – Mark Bolusmjak Jul 13 '16 at 20:29
  • `{ type = "leaf", home = "Task", value = Perform } : Platform.Cmd.Cmd Repl.Msg` how do you get the actual `Time`? – user2167582 Dec 11 '17 at 18:07
  • have a look at the Ellie linked to – Simon H Dec 11 '17 at 20:00
  • FYI, in Elm 0.19 `Time.now` has been moved to a separate package, [`elm/time`](https://package.elm-lang.org/packages/elm/time/latest/Time) – glennsl Jan 10 '19 at 15:38
  • 1
    Thanks, I have to say though this isn't even slightly easy, let alone getting easier. – Daniel Nov 04 '19 at 12:55
  • time and random numbers will always be hard in pure languages :-) – Simon H Nov 04 '19 at 14:35
11

You can use the Time package and/or the Date package.

Here's a contrived example which uses both:

import Signal
import Time exposing (every, second)
import Date exposing (year, hour, minute, second, fromTime)
import Graphics.Element exposing (show)

main =
  Signal.map currentTime (Time.every Time.second)

currentTime t =
  let date' = fromTime t
      hour' = toString (Date.hour date')
      minute' = toString (Date.minute date')
      second' = toString (Date.second date')
      year' = toString (year date')
      now = "The current time is: " ++ hour' ++ ":" ++ minute' ++ ":" ++ second'
  in 
      show now
pdoherty926
  • 9,895
  • 4
  • 37
  • 68
  • 1
    This unfortunately doesn't answer my own question, which is simply "how do I get the current time in elm". This is instead answering the question, "how can I execute a task on a time frequency basis, with the time being passed into it". Can this approach be adapted to solve the case of "call a function and it returns the current system time" ? – Tom Kludy Nov 05 '15 at 04:34
  • 1
    @TomKludy I don't think what you're requesting is possible, as that function would necessarily be impure. Per @Apanatshka's point, you could use a `Task` and pass a callback, which would receive the time after the system call had been made, or use a `Task` and a `Mailbox`, which result in the value being sent back into your program after it becomes available. See [Chaining Tasks](http://elm-lang.org/guide/reactivity#chaining-tasks) and [Communicating with Mailboxes](http://elm-lang.org/guide/reactivity#communicating-with-mailboxes) [here](http://elm-lang.org/guide/reactivity). – pdoherty926 Nov 05 '15 at 16:41
  • 10
    I think this is one of those specific things that would quickly turn away practitioners from using a functional language. You are writing you decoder to parse your incoming messages and basically you have no elegant way of sticking a timestamp onto them unless they already come with one. It feels a little bit like the smartphone where you can only call someone if you add her as a contact first.... – Gabor Dec 21 '15 at 13:36
  • 3
    @Gabor - anyone who has written a unit test to cover code that does a getSystemTime() call can attest it is difficult to test code that does something different when given the same parameters. With a functional approach unit-testing actually becomes joyful! Practicioners should love this. Ok fair enough you can use dependency injection or monkey patching but we know those solutions are less satisfactory. – Martin Capodici Feb 19 '16 at 05:21
  • @MartinCapodici Could you explain why there's an easy way to get a random number but not the current time? :P EDIT: I take that back. It's just that there is documentation on how to generate a random number. But not how to get the current time. – Daniel Nov 04 '19 at 13:01
8

To resolve my own question, I've created a variant of StartApp that includes a timestamp on each action.
So the update function has signature:
update : action -> Time -> model -> (model, Effects action)

The Gist is here. https://gist.github.com/z5h/41ca436679591b6c3e51

Mark Bolusmjak
  • 23,606
  • 10
  • 74
  • 129
  • A nice appoach indeed. I was thinking of doing something where you use a special value in the model for the time. However this appoach is working at the most abstract level and needs to know nothing about your app or your model. It is very elegant. – Martin Capodici Feb 19 '16 at 05:15
8

Elm 0.19

Below I set inital time as unix time start Time.millisToPosix 0, but you can set it to Nothing and later to Just time or pass it with Flag.

module Main exposing (main)

import Browser
import Html exposing (Html)
import Task
import Time exposing (Posix)


main : Program () Model Msg
main =
    Browser.element
        { init = \_ -> init
        , view = view
        , update = update
        , subscriptions = \_ -> Sub.none
        }



-- MODEL


type alias Model =
    { zone : Time.Zone
    , now : Posix
    }


init : ( Model, Cmd Msg )
init =
    ( Model Time.utc (Time.millisToPosix 0), Task.perform Zone Time.here )



-- UPDATE


type Msg
    = Zone Time.Zone
    | Now Posix


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Zone zone ->
            ( { model | zone = zone }, Task.perform Now Time.now )

        Now now ->
            ( { model | now = now }, Cmd.none )



-- VIEW


formatTime zone posix =
    (String.padLeft 2 '0' <| String.fromInt <| Time.toHour zone posix)
        ++ ":"
        ++ (String.padLeft 2 '0' <| String.fromInt <| Time.toMinute zone posix)
        ++ ":"
        ++ (String.padLeft 2 '0' <| String.fromInt <| Time.toSecond zone posix)


view : Model -> Html Msg
view model =
    Html.div []
        [ Html.text <| formatTime model.zone model.now
        ]
rofrol
  • 14,438
  • 7
  • 79
  • 77
5

If you want the time as of program start you can do the following:

Now.elm

module Now where

import Native.Now

loadTime : Float
loadTime = Native.Now.loadTime

Native/Now.js

Elm.Native.Now = {};

Elm.Native.Now.make = function(localRuntime) {

  localRuntime.Native = localRuntime.Native || {};


  localRuntime.Native.Now = localRuntime.Native.Now || {};

  if (localRuntime.Native.Now.values) {
    return localRuntime.Native.Now.values;
  }

  var Result = Elm.Result.make(localRuntime);

  return localRuntime.Native.Now.values = {
    loadTime: (new window.Date).getTime()
  };

};

your code

programStart = Now.loadTime
case nelson
  • 3,537
  • 3
  • 30
  • 37
  • I got this to work, which resolved my problem. Thanks! The key was to include the following in my elm-package.json: `"native-modules": true` – Tom Kludy Nov 09 '15 at 15:20
4

You can see pdoherty926's answer for how to do something with the current time in Elm.

elm-repl doesn't have the ability to work with Signals, and time "changes over time" so it's a signal. There also isn't a Task for getting the time, that I know of. Nor a way to execute tasks in the repl, though I expect that will be feature in the future.

Community
  • 1
  • 1
Apanatshka
  • 5,958
  • 27
  • 38
1

There are two main ways I can think of to work with the current time in Elm:

  1. Write / use a native module to make a function that returns the current time in ms (or returns a task that will do the same). This method isn't generally recommended. I think #2 is a better approach. But an example of #1 can be found here: https://github.com/evancz/task-tutorial/blob/1.0.2/src/TaskTutorial.elm (see the getCurrentTime function)

  2. Write a program using the Elm application architecture (https://github.com/evancz/elm-architecture-tutorial/), and then feed the current time signal as an input to the update cycle, updating the model with the new current time at every interval of your choosing. Then all of your other methods can just fetch the current time synchronously off of the model.

Murphy Randle
  • 816
  • 10
  • 19
  • 1
    Expanding on idea #2, which I think is best: `type Action = Tick Time | ...` and in update: `Tick time -> {model | time = time}`. – mgold Mar 11 '16 at 17:47
0

The answer (for 0.18) from Simon H got me started in the right direction, but I did have some trouble working out how to actually do something with that time. (user2167582 adds a comment to Simon's answer which asks the same thing: how do you 'get the time out?').

My specific problem was that I wanted to include the current time in the body of a POST to the server.

I eventually solved that and was quite pleased with the end result -- the use of Task.andThen meant that I in my postTime function I can just use timestamp as a 'regular' float-valued parameter (which it is when the task gets run, I suppose).

My full SO answer is here.

Below is the solution I came up with and here it is in Ellie:

module Main exposing (..)

import Html exposing (..)
import Html.Events exposing (..)
import Http
import Json.Decode as JD
import Json.Encode as JE
import Task
import Time


type alias Model =
    { url : String
    }


type Msg
    = PostTimeToServer
    | PostDone (Result Http.Error String)


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        PostTimeToServer ->
            ( model, postTimeToServer model.url )

        PostDone _ ->
            ( model, Cmd.none )


view : Model -> Html Msg
view model =
    div []
        [ div []
            [ button [ onClick PostTimeToServer ] [ Html.text "POST the current time." ]
            ]
        ]


postTimeToServer : String -> Cmd Msg
postTimeToServer url =
    let
        getTime =
            Time.now

        postTime t =
            JD.string
                |> Http.post url (JE.float t |> Http.jsonBody)
                |> Http.toTask

        request =
            getTime                                            <<-- Here is
                |> Task.andThen postTime                       <<-- the key bit.
    in
        Task.attempt PostDone request


main =
    Html.program
        { init = ( Model "url_here", Cmd.none )
        , update = update
        , view = view
        , subscriptions = always Sub.none
        }
Robert
  • 1,530
  • 1
  • 16
  • 24