使用Haskell / cassava将UTCTime转换为CSV字段

时间:2015-07-06 09:15:08

标签: sqlite csv haskell

我开始学习Haskell,我尝试从SQLite文件中查询数据并将其作为CSV再次转储。我在编制UTCTime字段时遇到问题,因此cassava可以正确格式化。这是我的类型:

    module History.Types
      (Visit)
      where

    import Data.ByteString as B
    import Data.Csv
    import Data.Text (Text)
    import Data.Time
    import Data.Time.Format
    import Database.SQLite.Simple.FromRow

    data Visit = Visit
      { url :: Text
      , title :: Text
      , visit_count :: Int
      , typed_count :: Int
      , last_visit_time :: UTCTime
      , visit_time :: UTCTime
      , from_visit :: Int
      }
      deriving (Show)

    instance FromRow Visit where
      fromRow = Visit <$> field
                      <*> field
                      <*> field
                      <*> field
                      <*> field
                      <*> field
                      <*> field

    instance ToField UTCTime where
      toField t = formatTime defaultTimeLocale "%d-%m-%Y %H:%M:%S" t

    instance ToRecord Visit where
      toRecord (Visit url
                      title
                      visit_count
                      typed_count
                      last_visit_time
                      visit_time
                      from_visit) = record [ toField visit_time
                                           , toField url
                                           , toField title
                                           , toField visit_count
                                           , toField typed_count
                                           , toField from_visit
                                           , toField last_visit_time
                                           ]

我的SQLite代码:

    {-# LANGUAGE OverloadedStrings #-}

    module History.DB
      (queryHistory)
      where

    import History.Types (Visit)

    import Data.Text (Text)
    import Database.SQLite.Simple (open, query_, close, Query)

    q :: Query
    q = "\
    \SELECT urls.url \
    \     , urls.title \
    \     , urls.visit_count \
    \     , urls.typed_count \
    \     , datetime(urls.last_visit_time/1000000-11644473600,'unixepoch','localtime') \
    \     , datetime(visits.visit_time/1000000-11644473600,'unixepoch','localtime') \
    \     , visits.from_visit \
    \FROM urls, visits \
    \WHERE urls.id = visits.id \
    \ORDER BY visits.visit_time"

    queryHistory :: FilePath -> IO [Visit]
    queryHistory dbPath =
      do conn <- open dbPath
         history <- query_ conn q
         close conn
         return history

编译此代码会导致以下错误:

    src/History/Types.hs:34:15:
        Couldn't match type ‘[Char]’ with ‘ByteString’
          Expected type: Field
          Actual type: String
        In the expression:
          formatTime defaultTimeLocale "%d-%m-%Y %H:%M:%S" t
        In an equation for ‘toField’:
          toField t = formatTime defaultTimeLocale "%d-%m-%Y %H:%M:%S" t

在将日期类型格式化为字符串时,我很清楚。我查看了formatTime的类型信息,并且不太明白为什么我的String(我假设错误与日期格式化字符串有关)必须是ByteString。所以我的问题是:

  1. 错误是否与时间格式化字符串有关,如果是,为什么常规字符串不起作用?
  2. 这是使用cassava格式化要写入CSV文件的日期类型的正确方法吗?

2 个答案:

答案 0 :(得分:2)

您的问题是toField有签名a -> Field。如果您查看Field类型,其定义为type Field = ByteStringformatTime的结果是String。所以问题是你提供String,其中ByteString是预期的。

由于String是一种非常常见的类型,因此您应该首先检查是否已有可用的ToField String实例。如果你看一下the definition。这意味着您可以将toField函数专门化为签名String -> Field并使用它:

instance ToField UTCTime where
      toField = toField . formatTime defaultTimeLocale "%d-%m-%Y %H:%M:%S"

答案 1 :(得分:0)

您是否在History.Types文件中尝试过{ - #LANGUAGE OverloadedStrings# - }?不确定这是否有用,但也许值得一试。

ToField必须返回ByteString,因为它返回一个Field,而Field是一个ByteString:

http://haddock.stackage.org/lts-2.17/cassava-0.4.3.0/Data-Csv.html#t:Field

您正在正确调用formatTime,它只是formatTime返回一个String而不是ByteString。

您可以使用fromString or pack直接转换。所以只需像现在一样调用formatTime并转换结果。