1

I'm getting quite lost in all the different date and time libraries available to me in Haskell. What I'm trying to do is format the file modification time as "yyyy-MM-dd". Can someone show me how to do that?

I've got this code-snippet:

main = do
  times <- mapM getModificationTime $ dataFiles config -- dataFiles returns [FilePath].
  -- I'd like to do something like the following:
  (putStr . unlines . map formatDate) times

This is what I've attempted. Please don't laugh!

formatDate t =
  let (year, month, day) = toGregorian $ utctDay t
   in intercalate "-" $ map show [year, fromIntegral month, fromIntegral day]

I know it's not completely up to my specifications yet, because I won't get months and days left-filled with zero. I can fix that. But I figure there's a more elegant way to get what I'm after, which is why I've titled my post with what I'm after rather than the current error I'm getting, which, for the sake of completeness is Couldn't match expected type 'UTCTime' with actual type 'ClockTime'.

Thanks for whatever help you can give or clarity you can provide.

Jeff Maner
  • 1,179
  • 9
  • 23
  • Could you clarify what type ghci reports for `getModificationTime`? (In ghci, run `:m +System.Directory` then `:t getModificationTime`.) Its type appears to have changed recently from `FilePath -> IO ClockTime` to `FilePath -> IO UTCTime`. – dave4420 May 14 '13 at 18:01
  • On my system, ghci says `getModificationTime :: FilePath -> IO System.Time.ClockTime`. But, yeah, I saw online that it returns `IO UTCTime`. Maybe that's part of my problem. – Jeff Maner May 14 '13 at 18:41

2 Answers2

2

Here's one approach:

λ> :m + Data.Time
λ> :m + System.Locale
λ> t <- getModificationTime "amy.hs"
λ> formatTime defaultTimeLocale "%Y-%m-%d" t
"2013-05-14"

You'll need the old-locale package. Despite the name, that is the current one as far as I know. There is no new-locale or locale!

mhwombat
  • 8,026
  • 28
  • 53
  • Love the lambda prompt! I get `No instance for (Data.Time.Format.FormatTime System.Time.ClockTime) arising from a use of 'formatTime'`. – Jeff Maner May 14 '13 at 19:42
1

Something like this should convert the ClockTime into a UTCTime:

import System.Time
import Data.Time.Clock.POSIX

utcTimeFromClockTime :: ClockTime -> UTCTime
utcTimeFromClockTime (TOD seconds picoseconds)
   = posixSecondsToUTCTime . fromRational
        $ fromInteger seconds + fromInteger picoseconds / 1000000000000

You probably want to convert that to use your local time zone. Use utcToLocalZonedTime :: UTCTime -> IO ZonedTime from Data.Time.LocalTime.

Either way, you then have a timestamp you can format:

import Data.Time.Format
import System.Locale

formatIsoDate :: FormatTime t => t -> String
formatIsoDate = formatTime defaultTimeLocale "%F"

(Sorry, I've not tested any of this code.)

dave4420
  • 46,404
  • 6
  • 118
  • 152
  • With this code, I get the following two errors that I can't figure out myself: `Couldn't match expected type 'Rational' with the actual type 'Integer' in the first argument of '(+)', namely 'seconds'.` and `Couldn't match expected type 'IO t0' with actual type 'UTCTime -> IO TimeZone' in a stmt of a 'do' block: timeZone <- getTimeZone`. – Jeff Maner May 16 '13 at 20:24
  • @JeffManer Sorry. I've edited the code in my answer, hopefully it will work now. – dave4420 May 17 '13 at 07:18
  • Got it! Thank you so much. Got an error trying to use utcToLocalZonedTime--but I don't need the time, so I just didn't use that. Thanks again! – Jeff Maner May 17 '13 at 15:40