简体   繁体   English

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

[英]Convert UTCTime to CSV field with Haskell/cassava

I started learning Haskell and I try to query data from a SQLite file and dump it again as CSV. 我开始学习Haskell,并尝试从SQLite文件中查询数据并将其再次转储为CSV。 I have an issue formating UTCTime fields, so that cassava can format it correctly. 我在格式化UTCTime字段时遇到问题,因此cassava可以正确格式化它。 This is my type: 这是我的类型:

    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
                                           ]

And my SQLite code: 和我的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

Compiling this code leads to the following error: 编译此代码会导致以下错误:

    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

So clearly I make a mess with when formatting the date type into a string. 所以很显然,当将日期类型格式化为字符串时,我会感到一团糟。 I look at the type information of formatTime and don't really understand why my String (I assume the error relates to the date formatting string) has to be a ByteString . 我查看formatTime的类型信息, formatTime并不太了解为什么我的String (我认为错误与日期格式字符串有关)必须是ByteString So my questions are: 所以我的问题是:

  1. Is the error related to the time formatting string, and if yes, why is a regular string not working? 错误与时间格式字符串有关吗?如果是,为什么常规字符串不起作用?
  2. Is this the right way to format date types to be written to a CSV file using cassava ? 这是使用cassava格式化要写入CSV文件的日期类型的正确方法吗?

Your problem is that toField has the signature a -> Field . 您的问题是toField具有签名toField a -> Field If you look at the Field type its definition is type Field = ByteString . 如果查看Field类型,则其定义为type Field = ByteString The result of formatTime is a String . formatTime的结果是String So the problem is that you provide a String where a ByteString is expected. 因此,问题在于您提供了一个需要ByteStringString

Since String is a very common type the first thing you should do is to check if there is already an instance for ToField String you can use. 由于String是非常常见的类型,因此您应该做的第一件事是检查是否已经可以使用ToField String的实例。 If you look at the definition there is. 如果您看一下定义 ,就会发现。 This means that you can specialize the toField function to have the signature String -> Field and the use it like this: 这意味着您可以专门使toField函数具有签名toField String -> Field并像这样使用它:

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

Have you tried {-# LANGUAGE OverloadedStrings #-} in your History.Types file? 您是否在History.Types文件中尝试过{-#LANGUAGE OverloadedStrings#-}? Not sure if that would work, but maybe worth a try. 不知道这是否行得通,但也许值得一试。

ToField has to return a ByteString because it returns a Field, and a Field IS a ByteString: ToField必须返回一个ByteString,因为它返回一个Field,而一个Field是一个ByteString:

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

You are calling formatTime correctly, its just that formatTime returns a String and not a ByteString. 您正在正确地调用formatTime,只是formatTime返回一个String而不是一个ByteString。

You can directly convert with fromString or pack . 您可以直接使用fromString或pack进行转换。 So just call formatTime like you are now and convert the result. 因此,像现在一样调用formatTime并转换结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM