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