Moments in time

Here we demonstrate getting the current time, comparing two moments in time, and converting between a few different timestamp representations.

None of the code here involves time zones. This program will not display the same current time as the clock on your wall, but it will behave the same regardless of what part of the world it runs in. This makes the types in this example suitable for a “machine” view of time such as you might use for recording the times of events in a log file, or for scheduling tasks in a multi-threaded or distributed application.

In this example we’ve included some optional type annotations for clarity. The scoped type variables language extension is required to let us write type annotations in a pattern context (on the left-hand side of either the = or <- symbol).

{-# LANGUAGE ScopedTypeVariables #-}

All of our imports here come from the time package.

import qualified Data.Time as T
import qualified Data.Time.Clock.POSIX as T

We will convert timestamps to their string representations using the formatTime function from the time library.

timeToString format time =
    T.formatTime T.defaultTimeLocale format time

We will parse strings as timestamp values using the parseTimeM function from the time library.

stringToTime format string =
    T.parseTimeM acceptExtraWhitespace
      T.defaultTimeLocale format string
  where
    acceptExtraWhitespace = False
main =
  do

The getCurrentTime action provides the current time from the system clock as a UTCTime value. A value of the UTCTime type represents a moment in time according to the coordinated universal time standard.

    (now_utc :: T.UTCTime) <- T.getCurrentTime
    putStrLn ("Now (UTC): " ++ show now_utc)

The getPOSIXTime action provides the current time from the system clock as a Unix timestamp, which is another common format that represents the same concept – a moment in time.

    (now_posix :: T.POSIXTime) <- T.getPOSIXTime
    putStrLn ("Now (POSIX): " ++ show now_posix)

Sometimes we receive a time in some string format that we need to interpret by parsing the string.

    let t1_string = "2038-01-19 03:14:07"

We use a format string (in this case, “%Y-%m-%d %H:%M:%S”) to specify how to parse the timestamp. See the Data.Time.Format module for documentation on how to write a format string.

    (t1_utc :: T.UTCTime) <-
        stringToTime "%Y-%m-%d %H:%M:%S" t1_string
    putStrLn (show t1_utc)

We also use format strings to convert a UTCTime value to a String. Here we use the format “%Y-%m-%d” to print the date portion of the timestamp, and then “%I:%M %p” to print the time of day in a 12-hour clock style.

    putStrLn (timeToString "%Y-%m-%d" t1_utc)
    putStrLn (timeToString "%I:%M %p" t1_utc)

Use addUTCTime to add some offset to a timestamp. Here we define t2_utc to be a time that is 15 seconds into the future from t1_utc.

    let (t2_utc :: T.UTCTime) = T.addUTCTime 15 t1_utc
    putStrLn (show t2_utc)
    putStrLn (show (t1_utc < t2_utc))

Use diffUTCTime to subtract one time from another and determine the amount of time elapsed from one moment to the next.

    let (diff :: T.NominalDiffTime) = T.diffUTCTime t2_utc t1_utc
    putStrLn (show diff)

You can convert from UTCTime to POSIXTime and vice versa.

    let (t1_posix :: T.POSIXTime) = T.utcTimeToPOSIXSeconds t1_utc
    putStrLn (show t1_posix)
    putStrLn (show (T.posixSecondsToUTCTime t1_posix))
$ runhaskell moments-in-time.hs

POSIX time is simpler, but UTC is easier to read at a glance.

Now (UTC): 2019-11-22 21:25:38.033996711 UTC
Now (POSIX): 1574457938.034344455s
2038-01-19 03:14:07 UTC
2038-01-19
03:14 AM
2038-01-19 03:14:22 UTC
True

“15s” signifies “15 seconds”.

15s
2147483647s
2038-01-19 03:14:07 UTC