text-iso8601 0.1.1.1 → 0.2.0.0
raw patch · 4 files changed
+39/−17 lines, 4 filesnew-uploader
Files
- changelog.md +4/−0
- src/Data/Time/FromText.hs +11/−4
- tests/text-iso8601-tests.hs +21/−10
- text-iso8601.cabal +3/−3
changelog.md view
@@ -1,3 +1,7 @@+# 0.2.0.0 - 2026-05-21++- Fix parsers to reject years with more than 15 digits+ # 0.1.1 - Support GHC-8.6.5...9.10.1
src/Data/Time/FromText.hs view
@@ -272,19 +272,24 @@ start :: Int -> Int -> Either String r start !off !len = unconsAscii_ arr off len (unexpectedEOF "-, +, or a digit") $ \c off' len' -> case c of- '-' -> loop negate off' off' len'- '+' -> loop id off' off' len'+ -- year is capped to 15 digits and we try consuming one more before raising an error, hence max 16 digits+ '-' -> loop negate off' off' (min len' 16)+ '+' -> loop id off' off' (min len' 16) _- | '0' <= c, c <= '9' -> loop id off off' len'+ | '0' <= c, c <= '9' -> loop id off off' (min len' 15) -- already consumed first digit, 15 left | otherwise -> Left $ "Unexpected '" ++ show c ++ ", expecting -, +, or a digit" + -- The caller must make sure not to consume too many digits, to avoid complexity blow up in conversion to Integer! loop :: (Integer -> Integer) -> Int -> Int -> Int -> Either String r loop !posNeg !off0 !off !len = unconsAscii_ arr off len (finishEOF posNeg off0 off) $ \c off' len' -> if | '0' <= c, c <= '9' -> loop posNeg off0 off' len'- | otherwise -> finishC posNeg c off0 off off' len'+ | otherwise -> finishC posNeg c off0 off off' (lenS - (off' - offS)) -- restore the full remaining length finishEOF :: (Integer -> Integer) -> Int -> Int -> Either String r finishEOF !posNeg !off0 !off+ | len0 >= 16+ = Left "expected year with at most 15 digits"+ | len0 >= 4 = year `seq` kontEOF year @@ -415,6 +420,8 @@ makeTimeOfDay h m s kont = if h < 24 && m < 60 && s < 61 then inline kont (Local.TimeOfDay h m s)+ else if h == 24 && m == 0 && s == 0+ then inline kont (Local.TimeOfDay 24 0 0) else Left $ "Invalid time of day:" ++ show (h,m,s) -- Parse seconds: @SS.SSS@.
tests/text-iso8601-tests.hs view
@@ -12,7 +12,7 @@ property) import Test.QuickCheck.Instances () import Test.Tasty (TestTree, defaultMain, testGroup)-import Test.Tasty.HUnit (assertFailure, testCase)+import Test.Tasty.HUnit (assertEqual, assertFailure, testCase) import Test.Tasty.QuickCheck (testProperty) import qualified Data.Text as T@@ -61,23 +61,34 @@ -- accepts +23:59 , accepts T.parseUTCTime "1937-01-01T12:00:00+23:59" , accepts T.parseUTCTime "1937-01-01T12:00:00-23:59"++ -- accepts 24:00:00+ , accepts T.parseUTCTime "1990-12-31T24:00:00Z" ] , testGroup "rejected" -- https://github.com/haskell/aeson/issues/1033- [ rejects T.parseUTCTime "2023-06-09T02:35:33 Z"+ [ rejects T.parseUTCTime "2023-06-09T02:35:33 Z" "Unexpected ' ', expecting timezone: Z, +HH:MM or -HH:MM" -- Y2K years- , rejects T.parseDay "99-12-12"+ , rejects T.parseDay "99-12-12" "expected year with at least 4 digits" -- we don't accept lowercase T or Z -- RFC3339 says we MAY limit, i.e. requiring they should be uppercase.- , rejects T.parseUTCTime "2023-06-09T02:35:33z"- , rejects T.parseUTCTime "2023-06-09t02:35:33Z"+ , rejects T.parseUTCTime "2023-06-09T02:35:33z" "Unexpected 'z', expecting timezone: Z, +HH:MM or -HH:MM"+ , rejects T.parseUTCTime "2023-06-09t02:35:33Z" "Unexpected 't', expecting a day separator, T or space" -- accepts +23:59, but not 24 or 60- , rejects T.parseUTCTime "1937-01-01T12:00:00+24:59"- , rejects T.parseUTCTime "1937-01-01T12:00:00-23:60"+ , rejects T.parseUTCTime "1937-01-01T12:00:00+24:59" "Invalid TimeZone:(24,59)"+ , rejects T.parseUTCTime "1937-01-01T12:00:00-23:60" "Invalid TimeZone:(23,60)"++ -- rejects 24:xx:xx except 24:00:00+ , rejects T.parseUTCTime "1990-12-31T24:00:01Z" "Invalid time of day:(24,0,1.000000000000)"+ , rejects T.parseUTCTime "1990-12-31T24:00:60Z" "Invalid time of day:(24,0,60.000000000000)"+ , rejects T.parseUTCTime "1990-12-31T24:01:00Z" "Invalid time of day:(24,1,0.000000000000)"++ -- Reject long years+ , rejects T.parseUTCTime "1234567890123456-01-01T01:01Z" "expected year with at most 15 digits" ] ] @@ -98,10 +109,10 @@ counterexample (show y) $ property (liftEq eq y (Right x)) -rejects :: forall a. (Typeable a, Show a) => (Text -> Either String a) -> String -> TestTree-rejects parse inp = testCase (show (typeRep (Proxy :: Proxy a)) ++ " rejects " ++ show inp) $ do+rejects :: forall a. (Typeable a, Show a) => (Text -> Either String a) -> String -> String -> TestTree+rejects parse inp expected = testCase (show (typeRep (Proxy :: Proxy a)) ++ " rejects " ++ show inp) $ do case parse (T.pack inp) of- Left _ -> return ()+ Left actual -> assertEqual "Error message mismatch" actual expected Right a -> assertFailure $ "Unexpectedly accepted: " ++ show a accepts :: forall a. (Typeable a, Show a) => (Text -> Either String a) -> String -> TestTree
text-iso8601.cabal view
@@ -1,9 +1,9 @@ cabal-version: 1.12 name: text-iso8601-version: 0.1.1.1+version: 0.2.0.0 synopsis: Converting time to and from ISO 8601 text. description:- Converting time to and from IS0 8601 text.+ Converting time to and from ISO 8601 text. Specifically the [RFC3339](https://datatracker.ietf.org/doc/html/rfc3339) profile. license: BSD3@@ -26,7 +26,7 @@ || ==9.4.8 || ==9.6.6 || ==9.8.4- || ==9.10.1+ || ==9.10.3 || ==9.12.4 || ==9.14.1