diff --git a/MANUAL.md b/MANUAL.md index b3607c7ed..a1ae71f08 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -290,6 +290,8 @@ historical price directive (P) as shown: expenses:foreign currency €100 assets +Note: a time and numeric time zone are allowed in historical price directives, but currently ignored. + Or, you can write a transaction in two commodities, without prices but with all amounts specified, and a conversion price will be inferred so as to balance the transaction: diff --git a/hledger-lib/Hledger/Read/JournalReader.hs b/hledger-lib/Hledger/Read/JournalReader.hs index 679d276dc..7fe0e78e2 100644 --- a/hledger-lib/Hledger/Read/JournalReader.hs +++ b/hledger-lib/Hledger/Read/JournalReader.hs @@ -123,8 +123,11 @@ import Data.List import Data.List.Split (wordsBy) import Data.Maybe import Data.Time.Calendar +-- import Data.Time.Clock +-- import Data.Time.Format import Data.Time.LocalTime import Safe (headDef) +-- import System.Locale (defaultTimeLocale) import Test.HUnit import Text.ParserCombinators.Parsec hiding (parse) import Text.Printf @@ -364,6 +367,8 @@ ledgerTransaction = do postings <- ledgerpostings return $ txnTieKnot $ Transaction date edate status code description comment md postings "" +-- | Parse a date in YYYY/MM/DD format. Fewer digits are allowed. The year +-- may be omitted if a default year has already been set. ledgerdate :: GenParser Char JournalContext Day ledgerdate = do -- hacky: try to ensure precise errors for invalid dates @@ -383,6 +388,10 @@ ledgerdate = do Just date -> return date "full or partial date" +-- | Parse a date and time in YYYY/MM/DD HH:MM[:SS][+-ZZZZ] format. Any +-- timezone will be ignored; the time is treated as local time. Fewer +-- digits are allowed, except in the timezone. The year may be omitted if +-- a default year has already been set. ledgerdatetime :: GenParser Char JournalContext LocalTime ledgerdatetime = do day <- ledgerdate @@ -394,14 +403,22 @@ ledgerdatetime = do m <- many1 digit let m' = read m guard $ m' >= 0 && m' <= 59 - s <- optionMaybe $ do - char ':' - many1 digit + s <- optionMaybe $ char ':' >> many1 digit let s' = case s of Just sstr -> read sstr - Nothing -> 0 + Nothing -> 0 guard $ s' >= 0 && s' <= 59 - let tod = TimeOfDay h' m' (fromIntegral s') - return $ LocalTime day tod + {- tz <- -} + optionMaybe $ do + plusminus <- oneOf "-+" + d1 <- digit + d2 <- digit + d3 <- digit + d4 <- digit + return $ plusminus:d1:d2:d3:d4:"" + -- ltz <- liftIO $ getCurrentTimeZone + -- let tz' = maybe ltz (fromMaybe ltz . parseTime defaultTimeLocale "%z") tz + -- return $ localTimeToUTC tz' $ LocalTime day $ TimeOfDay h' m' (fromIntegral s') + return $ LocalTime day $ TimeOfDay h' m' (fromIntegral s') ledgereffectivedate :: Day -> GenParser Char JournalContext Day ledgereffectivedate actualdate = do @@ -729,13 +746,20 @@ tests_Hledger_Read_JournalReader = TestList [ assertParse (parseWithCtx nullctx{ctxYear=Just 2011} ledgerdate "1/1") ,"ledgerdatetime" ~: do - assertParseFailure (parseWithCtx nullctx ledgerdatetime "2011/1/1") - assertParseFailure (parseWithCtx nullctx ledgerdatetime "2011/1/1 24:00:00") - assertParseFailure (parseWithCtx nullctx ledgerdatetime "2011/1/1 00:60:00") - assertParseFailure (parseWithCtx nullctx ledgerdatetime "2011/1/1 00:00:60") - assertParse (parseWithCtx nullctx ledgerdatetime "2011/1/1 00:00") - assertParse (parseWithCtx nullctx ledgerdatetime "2011/1/1 23:59:59") - assertParse (parseWithCtx nullctx ledgerdatetime "2011/1/1 3:5:7") + let p = do {t <- ledgerdatetime; eof; return t} + bad = assertParseFailure . parseWithCtx nullctx p + good = assertParse . parseWithCtx nullctx p + bad "2011/1/1" + bad "2011/1/1 24:00:00" + bad "2011/1/1 00:60:00" + bad "2011/1/1 00:00:60" + good "2011/1/1 00:00" + good "2011/1/1 23:59:59" + good "2011/1/1 3:5:7" + -- timezone is parsed but ignored + let startofday = LocalTime (fromGregorian 2011 1 1) (TimeOfDay 0 0 (fromIntegral 0)) + assertParseEqual (parseWithCtx nullctx p "2011/1/1 00:00-0800") startofday + assertParseEqual (parseWithCtx nullctx p "2011/1/1 00:00+1234") startofday ,"ledgerDefaultYear" ~: do assertParse (parseWithCtx nullctx ledgerDefaultYear "Y 2010\n") diff --git a/tests/timezone.test b/tests/timezone.test new file mode 100644 index 000000000..08eeac662 --- /dev/null +++ b/tests/timezone.test @@ -0,0 +1,21 @@ +# timezone-related tests +# 1. as in ledger, historical prices may contain a time and timezone. +# hledger ignores them and uses 00:00 local time instead. +bin/hledger -f - balance --no-total --cost +<<< +P 2011/01/01 00:00:00 A $1 +P 2011/01/01 15:00:00-0100 A $2 + +2010/12/31 + (20101231) 1 A + +2011/1/1 + (20110101) 1 A + +2011/1/2 + (20110102) 1 A +>>> + 1 A 20101231 + $2 20110101 + $2 20110102 +>>>=0