beyarkay
beyarkay

Reputation: 1023

Haskell get current time as ISO8601 string

I've tried several things I could find online to print the current time as an ISO8601 string, but none of them have worked:

  1. This SO answer assumes you already know how to get the current time, but I can't figure that out.

    I keep on getting an error Could not find module ‘System.Locale’ because it seems like System.Locale is deprecated/not installed by default anymore?

    I tried installing old-local but that just gave me new errors, I think caused by incompatibilities between time and old-locale:

    • Couldn't match expected type ‘time-1.9.3:Data.Time.Format.Locale.TimeLocale’
                  with actual type ‘System.Locale.TimeLocale’
      NB: ‘time-1.9.3:Data.Time.Format.Locale.TimeLocale’
            is defined in ‘Data.Time.Format.Locale’ in package ‘time-1.9.3’
          ‘System.Locale.TimeLocale’
            is defined in ‘System.Locale’ in package ‘old-locale-1.0.0.7’
    • In the first argument of ‘formatTime’, namely ‘defaultTimeLocale’
      In the expression: formatTime defaultTimeLocale "%FT%T%QZ"
      In an equation for ‘iso8601’:
          iso8601 = formatTime defaultTimeLocale "%FT%T%QZ"
       |
    49 | iso8601 = formatTime defaultTimeLocale "%FT%T%QZ"
       |                      ^^^^^^^^^^^^^^^^^
    
    
  2. This wiki page gets the current time, but doesn't format it as ISO8601.

  3. this blog post seems really good, but only has one section on formatting which is actually the same as 1.:

Prelude Data.Time> formatTime defaultTimeLocale "%T, %F (%Z)" myTime

What I want, is to be able to call a function have it print out the current time as an ISO8601 string. I'm guessing this will have to actually be an IO action since it's not a strict function.

Upvotes: 4

Views: 471

Answers (4)

parkerbrads
parkerbrads

Reputation: 116

I wonder if something from the time package’s Data.Time.Format.ISO8601 module might help? The iso8601Show function turns a UTCTime into a ISO8601 formatted string.

import Data.Time (getCurrentTime)
import Data.Time.Format.ISO8601 (iso8601Show)

main :: IO ()
main = do
  now <- getCurrentTime
  putStrLn (iso8601Show now)

and in the ghci:

>>> import Data.Time (getCurrentTime)
>>> import Data.Time.Format.ISO8601 (iso8601Show)
>>> now <- getCurrentTime
>>> putStrLn (iso8601Show now)
2022-09-09T08:06:21.630747Z

Upvotes: 5

lsmor
lsmor

Reputation: 5063

The show instance for UTC is what you want. Therefore you can just use show.

getCurrentTimeASISO8601 :: IO String
getCurrentTimeASISO8601 = fmap (take 23 . show) getCurrentTime

Just a note: Up to my best knowledge ISO8601 isn't yyyy-MM-dd hh:mm:ss but yyyy-MM-ddThh:mm:ss. So if you are implementing this based on the standard, check it twice.

Upvotes: 2

beyarkay
beyarkay

Reputation: 1023

I think I figured it out. Note that Haskell has funky string formatting time specifications

  • %F == year-month-day
  • %T == hour:minute:second
  • %Q == .up_to_12_decimals_for_fraction_of_a_second

import Data.Time.Format (defaultTimeLocale, formatTime)
import Data.Time.Clock

getCurrentTimeAsISO8601 :: IO String
getCurrentTimeAsISO8601 = do
  t <- getCurrentTime
  let val = formatTime defaultTimeLocale "%F %T%Q" t
  -- Take the first 23 characters so we don't get the microseconds
  return $ take 23 val

main = do
  time <- getCurrentTimeAsISO8601
  putStrLn time

You can use the online ghci here to copy-paste the code and it should all work.

Upvotes: 3

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476493

You can slighly improve this by performing a functor mapping:

import Data.Time.Format (defaultTimeLocale, formatTime)
import Data.Time.Clock(getCurrentTime)

getCurrentTimeAsISO8601 :: IO String
getCurrentTimeAsISO8601 = take 23 . formatTime defaultTimeLocale "%F %T%Q" <$> getCurrentTime

Upvotes: 2

Related Questions