Add support for date-format conversion directive

This commit is contained in:
Max Bolingbroke 2010-12-21 17:57:06 +00:00
parent 2c771f3d7f
commit 8c3aa657b6
3 changed files with 31 additions and 5 deletions

View File

@ -560,10 +560,15 @@ Notes:
- Definitions must come first, one per line, all in one - Definitions must come first, one per line, all in one
paragraph. Each is a name and a value separated by whitespace. paragraph. Each is a name and a value separated by whitespace.
Supported names are: base-account, date-field, status-field, Supported names are: base-account, date-field, date-format, status-field,
code-field, description-field, amount-field, currency-field, code-field, description-field, amount-field, currency-field,
currency. All are optional and will use defaults if not specified. currency. All are optional and will use defaults if not specified.
- The date-format field contains the expected format for the input dates:
this is the same as that accepted by the Haskell
[formatTime](http://hackage.haskell.org/packages/archive/time/latest/doc/html/Data-Time-Format.html#v:formatTime)
function.
- The remainder of the file is account-assigning rules. Each is a - The remainder of the file is account-assigning rules. Each is a
paragraph consisting of one or more description-matching patterns paragraph consisting of one or more description-matching patterns
(case-insensitive regular expressions), one per line, followed by (case-insensitive regular expressions), one per line, followed by

View File

@ -35,6 +35,7 @@ convert a particular CSV data file into meaningful journal transactions. See abo
-} -}
data CsvRules = CsvRules { data CsvRules = CsvRules {
dateField :: Maybe FieldPosition, dateField :: Maybe FieldPosition,
dateFormat :: Maybe String,
statusField :: Maybe FieldPosition, statusField :: Maybe FieldPosition,
codeField :: Maybe FieldPosition, codeField :: Maybe FieldPosition,
descriptionField :: Maybe FieldPosition, descriptionField :: Maybe FieldPosition,
@ -47,6 +48,7 @@ data CsvRules = CsvRules {
nullrules = CsvRules { nullrules = CsvRules {
dateField=Nothing, dateField=Nothing,
dateFormat=Nothing,
statusField=Nothing, statusField=Nothing,
codeField=Nothing, codeField=Nothing,
descriptionField=Nothing, descriptionField=Nothing,
@ -165,6 +167,7 @@ definitions :: GenParser Char CsvRules ()
definitions = do definitions = do
choice' [ choice' [
datefield datefield
,dateformat
,statusfield ,statusfield
,codefield ,codefield
,descriptionfield ,descriptionfield
@ -183,6 +186,13 @@ datefield = do
r <- getState r <- getState
setState r{dateField=readMay v} setState r{dateField=readMay v}
dateformat = do
string "date-format"
many1 spacenonewline
v <- restofline
r <- getState
setState r{dateFormat=Just v}
codefield = do codefield = do
string "code-field" string "code-field"
many1 spacenonewline many1 spacenonewline
@ -271,7 +281,7 @@ printTxn debug rules rec = do
transactionFromCsvRecord :: CsvRules -> CsvRecord -> Transaction transactionFromCsvRecord :: CsvRules -> CsvRecord -> Transaction
transactionFromCsvRecord rules fields = transactionFromCsvRecord rules fields =
let let
date = parsedate $ normaliseDate $ maybe "1900/1/1" (atDef "" fields) (dateField rules) date = parsedate $ normaliseDate (dateFormat rules) $ maybe "1900/1/1" (atDef "" fields) (dateField rules)
status = maybe False (null . strip . (atDef "" fields)) (statusField rules) status = maybe False (null . strip . (atDef "" fields)) (statusField rules)
code = maybe "" (atDef "" fields) (codeField rules) code = maybe "" (atDef "" fields) (codeField rules)
desc = maybe "" (atDef "" fields) (descriptionField rules) desc = maybe "" (atDef "" fields) (descriptionField rules)
@ -323,9 +333,11 @@ transactionFromCsvRecord rules fields =
in t in t
-- | Convert some date string with unknown format to YYYY/MM/DD. -- | Convert some date string with unknown format to YYYY/MM/DD.
normaliseDate :: String -> String normaliseDate :: Maybe String -- ^ User-supplied date format: this should be tried in preference to all others
normaliseDate s = maybe "0000/00/00" showDate $ -> String -> String
firstJust normaliseDate mb_user_format s = maybe "0000/00/00" showDate $
firstJust $
(maybe id (\user_format -> (parseTime defaultTimeLocale user_format s :)) mb_user_format) $
[parseTime defaultTimeLocale "%Y/%m/%e" s [parseTime defaultTimeLocale "%Y/%m/%e" s
-- can't parse a month without leading 0, try adding one -- can't parse a month without leading 0, try adding one
,parseTime defaultTimeLocale "%Y/%m/%e" (take 5 s ++ "0" ++ drop 5 s) ,parseTime defaultTimeLocale "%Y/%m/%e" (take 5 s ++ "0" ++ drop 5 s)

9
tests/convert.test Normal file
View File

@ -0,0 +1,9 @@
# Conversion from CSV to Ledger
printf 'base-account Assets:MyAccount\ndate-field 0\ndate-format %%d/%%Y/%%m\ndescription-field 1\namount-field 2\ncurrency $\n' >input.rules ; printf '10/2009/09,Flubber Co,50' > input.csv ; touch unused.journal ; bin/hledger -f unused.journal convert input.csv ; rm -rf unused.journal input.rules input.csv
>>>
2009/09/10 Flubber Co
income:unknown $-50
Assets:MyAccount $50
>>>2
using conversion rules file input.rules