我开始学习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
。所以我的问题是:
- 错误是否与时间格式化字符串有关,如果是,为什么常规字符串不起作用?
- 这是正确的方式来格式化日期类型写入使用
cassava
的CSV文件?
您的问题是toField
具有a -> Field
的签名。如果你看Field
类型,它的定义是type Field = ByteString
。formatTime
的结果是String
。因此,问题是您在需要ByteString
的地方提供了String
。
由于String
是一个非常常见的类型,你应该做的第一件事是检查是否已经有一个ToField String
的实例可以使用。如果你看一下定义。这意味着您可以专门化toField
函数,使其具有签名String -> Field
,并像这样使用它:
instance ToField UTCTime where
toField = toField . formatTime defaultTimeLocale "%d-%m-%Y %H:%M:%S"
您是否在历史记录中尝试过{-# LANGUAGE overloaddstrings #-} ?类型的文件?不确定这是否有效,但也许值得一试。
ToField必须返回一个ByteString,因为它返回一个字段,而一个字段是一个ByteString:
http://haddock.stackage.org/lts - 2.17 -/-木薯- 0.4.3.0/data csv.html # t:字段
你正确地调用了formatTime,只是formatTime返回一个String而不是一个ByteString。
可以直接使用fromString或pack进行转换。像现在这样调用formatTime并转换结果。