lib: Ensure parsed years have at least 4 digits.
This commit is contained in:
parent
7b9f9ae49c
commit
ffb5cf0773
@ -71,6 +71,7 @@ module Hledger.Data.Dates (
|
|||||||
fixSmartDateStr,
|
fixSmartDateStr,
|
||||||
fixSmartDateStrEither,
|
fixSmartDateStrEither,
|
||||||
fixSmartDateStrEither',
|
fixSmartDateStrEither',
|
||||||
|
yearp,
|
||||||
daysInSpan,
|
daysInSpan,
|
||||||
maybePeriod,
|
maybePeriod,
|
||||||
mkdatespan,
|
mkdatespan,
|
||||||
@ -84,6 +85,7 @@ import Control.Applicative (liftA2)
|
|||||||
import Control.Applicative.Permutations
|
import Control.Applicative.Permutations
|
||||||
import Control.Monad (guard, unless)
|
import Control.Monad (guard, unless)
|
||||||
import "base-compat-batteries" Data.List.Compat
|
import "base-compat-batteries" Data.List.Compat
|
||||||
|
import Data.Char (isDigit)
|
||||||
import Data.Default
|
import Data.Default
|
||||||
import Data.Foldable (asum)
|
import Data.Foldable (asum)
|
||||||
import Data.Function (on)
|
import Data.Function (on)
|
||||||
@ -763,12 +765,7 @@ Right (SmartYMD (Just 201813012) Nothing Nothing)
|
|||||||
smartdate :: TextParser m SmartDate
|
smartdate :: TextParser m SmartDate
|
||||||
smartdate = choice'
|
smartdate = choice'
|
||||||
-- XXX maybe obscures date errors ? see ledgerdate
|
-- XXX maybe obscures date errors ? see ledgerdate
|
||||||
[ yyyymmdd
|
[ yyyymmdd, md, ymd, smartYear, smartDay, month, mon
|
||||||
, md
|
|
||||||
, ymd
|
|
||||||
, yd
|
|
||||||
, month
|
|
||||||
, mon
|
|
||||||
, SmartRel This Day <$ string' "today"
|
, SmartRel This Day <$ string' "today"
|
||||||
, SmartRel Last Day <$ string' "yesterday"
|
, SmartRel Last Day <$ string' "yesterday"
|
||||||
, SmartRel Next Day <$ string' "tomorrow"
|
, SmartRel Next Day <$ string' "tomorrow"
|
||||||
@ -778,6 +775,11 @@ smartdate = choice'
|
|||||||
seqP = choice [This <$ string' "this", Last <$ string' "last", Next <$ string' "next"]
|
seqP = choice [This <$ string' "this", Last <$ string' "last", Next <$ string' "next"]
|
||||||
intervalP = choice [Day <$ string' "day", Week <$ string' "week", Month <$ string' "month",
|
intervalP = choice [Day <$ string' "day", Week <$ string' "week", Month <$ string' "month",
|
||||||
Quarter <$ string' "quarter", Year <$ string' "year"]
|
Quarter <$ string' "quarter", Year <$ string' "year"]
|
||||||
|
smartYear = (\y -> SmartYMD (Just y) Nothing Nothing) <$> yearp
|
||||||
|
smartDay = do
|
||||||
|
d <- SmartYMD Nothing Nothing . Just <$> decimal
|
||||||
|
failIfInvalidDate d
|
||||||
|
return d
|
||||||
|
|
||||||
-- | Like smartdate, but there must be nothing other than whitespace after the date.
|
-- | Like smartdate, but there must be nothing other than whitespace after the date.
|
||||||
smartdateonly :: TextParser m SmartDate
|
smartdateonly :: TextParser m SmartDate
|
||||||
@ -812,7 +814,7 @@ yyyymmdd = do
|
|||||||
|
|
||||||
ymd :: TextParser m SmartDate
|
ymd :: TextParser m SmartDate
|
||||||
ymd = do
|
ymd = do
|
||||||
y <- decimal
|
y <- yearp
|
||||||
sep <- datesepchar
|
sep <- datesepchar
|
||||||
m <- decimal
|
m <- decimal
|
||||||
d <- optional $ char sep *> decimal
|
d <- optional $ char sep *> decimal
|
||||||
@ -829,12 +831,13 @@ md = do
|
|||||||
failIfInvalidDate date
|
failIfInvalidDate date
|
||||||
return date
|
return date
|
||||||
|
|
||||||
yd :: TextParser m SmartDate
|
-- | Parse a year number from a Text, making sure that at least four digits are
|
||||||
yd = do
|
-- used.
|
||||||
n <- decimal
|
yearp :: TextParser m Integer
|
||||||
if n >= 1 && n <= 31
|
yearp = do
|
||||||
then return $ SmartYMD Nothing Nothing (Just $ fromInteger n)
|
year <- takeWhile1P (Just "year") isDigit
|
||||||
else return $ SmartYMD (Just n) Nothing Nothing
|
unless (T.length year >= 4) . Fail.fail $ "Year must contain at least 4 digits: " <> T.unpack year
|
||||||
|
return $ readDecimal year
|
||||||
|
|
||||||
-- These are compared case insensitively, and should all be kept lower case.
|
-- These are compared case insensitively, and should all be kept lower case.
|
||||||
months = ["january","february","march","april","may","june",
|
months = ["january","february","march","april","may","june",
|
||||||
|
|||||||
@ -98,7 +98,6 @@ import Data.Time.LocalTime
|
|||||||
import Safe
|
import Safe
|
||||||
import Text.Megaparsec hiding (parse)
|
import Text.Megaparsec hiding (parse)
|
||||||
import Text.Megaparsec.Char
|
import Text.Megaparsec.Char
|
||||||
import Text.Megaparsec.Char.Lexer (decimal)
|
|
||||||
import Text.Megaparsec.Custom
|
import Text.Megaparsec.Custom
|
||||||
import Text.Printf
|
import Text.Printf
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
@ -553,8 +552,7 @@ defaultyeardirectivep :: JournalParser m ()
|
|||||||
defaultyeardirectivep = do
|
defaultyeardirectivep = do
|
||||||
char 'Y' <?> "default year"
|
char 'Y' <?> "default year"
|
||||||
lift skipNonNewlineSpaces
|
lift skipNonNewlineSpaces
|
||||||
y <- decimal
|
setYear =<< lift yearp
|
||||||
setYear y
|
|
||||||
|
|
||||||
defaultcommoditydirectivep :: JournalParser m ()
|
defaultcommoditydirectivep :: JournalParser m ()
|
||||||
defaultcommoditydirectivep = do
|
defaultcommoditydirectivep = do
|
||||||
|
|||||||
@ -50,12 +50,14 @@ module Hledger.Utils.Text
|
|||||||
-- fitStringMulti,
|
-- fitStringMulti,
|
||||||
textPadLeftWide,
|
textPadLeftWide,
|
||||||
textPadRightWide,
|
textPadRightWide,
|
||||||
|
-- -- * Reading
|
||||||
|
readDecimal,
|
||||||
-- -- * tests
|
-- -- * tests
|
||||||
tests_Text
|
tests_Text
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
-- import Data.Char
|
import Data.Char (digitToInt)
|
||||||
import Data.List
|
import Data.List
|
||||||
#if !(MIN_VERSION_base(4,11,0))
|
#if !(MIN_VERSION_base(4,11,0))
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
@ -400,6 +402,13 @@ textWidth s = maximum $ map (T.foldr (\a b -> charWidth a + b) 0) $ T.lines s
|
|||||||
-- | otherwise -> 1
|
-- | otherwise -> 1
|
||||||
|
|
||||||
|
|
||||||
|
-- | Read a decimal number from a Text. Assumes the input consists only of digit
|
||||||
|
-- characters.
|
||||||
|
readDecimal :: Integral a => Text -> a
|
||||||
|
readDecimal = foldl' step 0 . T.unpack
|
||||||
|
where step a c = a * 10 + fromIntegral (digitToInt c)
|
||||||
|
|
||||||
|
|
||||||
tests_Text = tests "Text" [
|
tests_Text = tests "Text" [
|
||||||
test "quoteIfSpaced" $ do
|
test "quoteIfSpaced" $ do
|
||||||
quoteIfSpaced "a'a" @?= "a'a"
|
quoteIfSpaced "a'a" @?= "a'a"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user