Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing dates from csv with cassava

Tags:

haskell

I'm trying to parse a csv file with data like this. I can parse with the date as a string, but haven't been able to get them in a day format. I finally got this to type check, but now I have a parse error and am hoping for some help.

2001/12/18, 281
2001/12/19, 280
2001/12/20, 276
2001/12/21, 278

Here's my code so far.

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}

import qualified Data.ByteString.Lazy as BL
import Data.Csv
import qualified Data.Vector as V
import Data.Time
import Generics.Deriving

datadir="/home/john/.stack/test/data/"  

data Sample = Sample { dateMeasure :: !Data.Time.Day 
                 , valueMeasure :: !Int 
                 } deriving (Generic, Show)   

instance FromRecord Sample 

instance FromField Data.Time.Day where
    parseField = parseTimeM True defaultTimeLocale "%Y/%m/%d" . show 

printRecord :: Sample -> IO ()
printRecord r  = putStrLn $  show (dateMeasure r) ++ " measurement is " ++    show (valueMeasure r) 

main :: IO ()
main = do
    csvData <- BL.readFile $ datadir ++ "sample.csv"
    case decode NoHeader csvData :: Either String (V.Vector Sample) of
        Left err -> putStrLn err
        Right v -> V.forM_ v $ printRecord   

Here is the error I am getting

~/.stack/test/ stack exec test
parse error (Failed reading: conversion error: parseTimeM: no parse of "\"2001/12/18\"") at 
2001/12/19, 280
2001/12/20, 276
2001/12/21, 278
2001/12/26, 278
2001/12/27, 278
2001/12/28, 2 (truncated)
like image 336
John Avatar asked Feb 14 '16 20:02

John


2 Answers

You are using show, which wraps the value in double quotes. parseTimeM is receiving the value "\"2001/12/18\"", and it doesn't know expect the quotes at the beginning and end of the string. Remove those quotes and you should be fine.

like image 153
Chad Gilbert Avatar answered Oct 21 '22 21:10

Chad Gilbert


The question is pretty old, but I've just dealt with it myself, so let me post it here.

According to the manual, parseField is a function of type:

parseField :: Field -> Parser a 

In our case we want type a to be Day, while Field is just a type alias

type Field = ByteString

The function that you use:

parseTimeM True defaultTimeLocale "%Y/%m/%d"

Expects a String, not a ByteString. So in order to make everything correct, you need to provide an adapter function ByteString -> String.

Using show makes it all type check, but it has the flaw that it leaves the quotation marks in the resulting string. A better choice would be to use unpack function that is meant to perform the conversion between the types.

So the entire code is:

import Data.ByteString.Char8 (unpack)

instance FromField Data.Time.Day where
    parseField = parseTimeM True defaultTimeLocale "%Y/%m/%d" . unpack
like image 22
Alojzy Leszcz Avatar answered Oct 21 '22 20:10

Alojzy Leszcz