lib: refactor date parser
This commit is contained in:
parent
12e8d0e282
commit
93fbac99d3
@ -105,7 +105,6 @@ import Data.Default
|
||||
import Data.Functor.Identity
|
||||
import Data.List.Compat
|
||||
import Data.List.NonEmpty (NonEmpty(..))
|
||||
import Data.List.Split (wordsBy)
|
||||
import Data.Maybe
|
||||
import qualified Data.Map as M
|
||||
import Data.Text (Text)
|
||||
@ -116,6 +115,7 @@ import Data.Void (Void)
|
||||
import System.Time (getClockTime)
|
||||
import Text.Megaparsec
|
||||
import Text.Megaparsec.Char
|
||||
import Text.Megaparsec.Char.Lexer (decimal)
|
||||
|
||||
import Hledger.Data
|
||||
import Hledger.Utils
|
||||
@ -366,28 +366,39 @@ datep = do
|
||||
lift $ datep' myear
|
||||
|
||||
datep' :: Maybe Year -> TextParser m Day
|
||||
datep' myear = do
|
||||
-- hacky: try to ensure precise errors for invalid dates
|
||||
-- XXX reported error position is not too good
|
||||
-- pos <- genericSourcePos <$> getPosition
|
||||
datestr <- do
|
||||
c <- digitChar
|
||||
cs <- many $ choice' [digitChar, datesepchar]
|
||||
return $ c:cs
|
||||
let sepchars = nub $ sort $ filter (`elem` datesepchars) datestr
|
||||
when (length sepchars /= 1) $ fail $ "bad date, different separators used: " ++ datestr
|
||||
let dateparts = wordsBy (`elem` datesepchars) datestr
|
||||
[y,m,d] <- case (dateparts, myear) of
|
||||
([m,d],Just y) -> return [show y,m,d]
|
||||
([_,_],Nothing) -> fail $ "partial date "++datestr++" found, but the current year is unknown"
|
||||
([y,m,d],_) -> return [y,m,d]
|
||||
_ -> fail $ "bad date: " ++ datestr
|
||||
let maybedate = fromGregorianValid (read y) (read m) (read d)
|
||||
case maybedate of
|
||||
Nothing -> fail $ "bad date: " ++ datestr
|
||||
Just date -> return date
|
||||
datep' mYear = do
|
||||
d1 <- decimal <?> "year or month"
|
||||
sep <- satisfy isDateSepChar <?> "date separator"
|
||||
d2 <- decimal <?> "month or day"
|
||||
fullDate d1 sep d2 <|> partialDate mYear d1 sep d2
|
||||
<?> "full or partial date"
|
||||
|
||||
where
|
||||
|
||||
fullDate :: Integer -> Char -> Integer -> TextParser m Day
|
||||
fullDate year sep1 month = do
|
||||
sep2 <- satisfy isDateSepChar <?> "date separator"
|
||||
day <- decimal <?> "day"
|
||||
let dateStr = show year ++ [sep1] ++ show month ++ [sep2] ++ show day
|
||||
|
||||
when (sep1 /= sep2) $ fail $
|
||||
"invalid date (mixing date separators is not allowed): " ++ dateStr
|
||||
|
||||
case fromGregorianValid year (fromIntegral month) day of
|
||||
Nothing -> fail $ "well-formed but invalid date: " ++ dateStr
|
||||
Just date -> pure date
|
||||
|
||||
partialDate :: Maybe Year -> Integer -> Char -> Integer -> TextParser m Day
|
||||
partialDate mYear month sep day = case mYear of
|
||||
Just year ->
|
||||
case fromGregorianValid year (fromIntegral month) (fromIntegral day) of
|
||||
Nothing -> fail $ "well-formed but invalid date: " ++ dateStr
|
||||
Just date -> pure date
|
||||
where dateStr = show year ++ [sep] ++ show month ++ [sep] ++ show day
|
||||
Nothing -> fail $
|
||||
"partial date "++dateStr++" found, but the current year is unknown"
|
||||
where dateStr = show month ++ [sep] ++ show day
|
||||
|
||||
-- | Parse a date and time in YYYY/MM/DD HH:MM[:SS][+-ZZZZ] format.
|
||||
-- Hyphen (-) and period (.) are also allowed as date separators.
|
||||
-- The year may be omitted if a default year has been set.
|
||||
@ -1040,13 +1051,13 @@ bracketedpostingdatesp mdefdate = do
|
||||
-- Left ...not a bracketed date...
|
||||
--
|
||||
-- >>> first parseErrorPretty $ rtp (bracketeddatetagsp Nothing) "[2016/1/32]"
|
||||
-- Left ...1:11:...bad date: 2016/1/32...
|
||||
-- Left ...1:11:...well-formed but invalid date: 2016/1/32...
|
||||
--
|
||||
-- >>> first parseErrorPretty $ rtp (bracketeddatetagsp Nothing) "[1/31]"
|
||||
-- Left ...1:6:...partial date 1/31 found, but the current year is unknown...
|
||||
--
|
||||
-- >>> first parseErrorPretty $ rtp (bracketeddatetagsp Nothing) "[0123456789/-.=/-.=]"
|
||||
-- Left ...1:15:...bad date, different separators...
|
||||
-- Left ...1:13:...expecting month or day...
|
||||
--
|
||||
bracketeddatetagsp :: Maybe Day -> SimpleTextParser [(TagName, Day)]
|
||||
bracketeddatetagsp mdefdate = do
|
||||
|
||||
@ -5,7 +5,7 @@ hledger -f- print
|
||||
2010/31/12 x
|
||||
a 1
|
||||
b
|
||||
>>>2 /bad date/
|
||||
>>>2 /invalid date/
|
||||
>>>= 1
|
||||
# 2. too-large day
|
||||
hledger -f- print
|
||||
@ -13,7 +13,7 @@ hledger -f- print
|
||||
2010/12/32 x
|
||||
a 1
|
||||
b
|
||||
>>>2 /bad date/
|
||||
>>>2 /invalid date/
|
||||
>>>= 1
|
||||
# 3. 29th feb on leap year should be ok
|
||||
hledger -f- print
|
||||
@ -33,7 +33,7 @@ hledger -f- print
|
||||
2001/2/29 x
|
||||
a 1
|
||||
b
|
||||
>>>2 /bad date/
|
||||
>>>2 /invalid date/
|
||||
>>>= 1
|
||||
# 5. dates must be followed by whitespace or newline
|
||||
hledger -f- print
|
||||
|
||||
@ -50,5 +50,5 @@ end comment
|
||||
2000/1/2
|
||||
b 0 ; [1/1=1/2/3/4] bad second date, should error
|
||||
|
||||
>>>2 /9:25/
|
||||
>>>2 /9:23/
|
||||
>>>=1
|
||||
|
||||
Loading…
Reference in New Issue
Block a user