Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- fix gregorianPaschalMoon and gregorianEaster
- fix negative rollover differences in diffGregorianDurationRollOver and diffJulianDurationRollOver
- fix case-insensitive parsing for AM/PM markers, UTC, and known timezone names
- fix timezone offset parsing of malformed minutes and ISO8601 offset ranges
- fix Unix timezone fallback offset sign and DST abbreviation
- fix JavaScript getTimeZone offsets and summer flag around DST transitions
- fix numeric parsers accepting overflowing bounded values
Expand Down
24 changes: 15 additions & 9 deletions lib/Data/Time/Format/ISO8601.hs
Original file line number Diff line number Diff line change
Expand Up @@ -282,22 +282,28 @@ withUTCDesignator f = f <** specialCaseReadFormat ((), "") (literalFormat "Z")
timeOffsetFormat :: FormatExtension -> Format TimeZone
timeOffsetFormat fe =
let
toTimeZone (sign, ehm) =
minutesToTimeZone $
sign * case ehm of
Left h -> h * 60
Right (h, m) -> h * 60 + m
fromTimeZone tz =
toOffsetMinutes h m
| h < 0 || h > 24 = Nothing
| m < 0 || m > 59 = Nothing
| h == 24 && m /= 0 = Nothing
| otherwise = Just $ h * 60 + m
toTimeZone (sign, ehm) = do
offset <-
case ehm of
Left h -> toOffsetMinutes h 0
Right (h, m) -> toOffsetMinutes h m
return $ minutesToTimeZone $ sign * offset
fromTimeZone tz = do
let
mm = timeZoneMinutes tz
(h, m) = quotRem (abs mm) 60
in
(signum mm, Right (h, m))
_ <- toOffsetMinutes h m
return (signum mm, Right (h, m))
digits2 = integerFormat NoSign (Just 2)
in
specialCaseReadFormat (utc, "") $
specialCaseReadFormat (utc, "Z") $
isoMap toTimeZone fromTimeZone $
mapMFormat toTimeZone fromTimeZone $
mandatorySignFormat <**> (digits2 <++> extColonFormat fe digits2 digits2)

-- | @hh:mm:ss±hh:mm@ (extended), @hhmmss±hhmm@ (basic) [ISO 8601:2004(E) sec. 4.2.5.2]
Expand Down
2 changes: 1 addition & 1 deletion lib/Data/Time/Format/Parse/Instances.hs
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ readTzOffset str =
calc s h1 h2 m1 m2 = do
sign <- getSign s
h <- readMaybe [h1, h2]
m <- readMaybe [m1, m2]
m <- clipValid 0 59 =<< readMaybe [m1, m2]
return $ sign * (60 * h + m)
in
case str of
Expand Down
18 changes: 18 additions & 0 deletions test/main/Test/Format/ISO8601.hs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@
testReadFormatFails :: (Show t, Eq t) => String -> Format t -> String -> TestTree
testReadFormatFails name fmt str = nameTest (name ++ ": " ++ str) $ assertEqual "" Nothing $ formatParseM fmt str

testReadFormatFails :: (Show t, Eq t) => String -> Format t -> String -> TestTree
testReadFormatFails name fmt str = nameTest (name ++ ": " ++ str) $ assertEqual "" Nothing $ formatParseM fmt str

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-windows (9.14)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-windows (9.14)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-macos (9.10)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-windows (9.8)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-macos (9.10)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-windows (9.10)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-windows (9.8)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-windows (9.12)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-macos (9.14)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-windows (9.12)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-windows (9.10)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-macos (9.8)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-macos (9.8)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-macos (9.12)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-macos (9.14)

Multiple declarations of ‘testReadFormatFails’

Check failure on line 167 in test/main/Test/Format/ISO8601.hs

View workflow job for this annotation

GitHub Actions / build-macos (9.12)

Multiple declarations of ‘testReadFormatFails’

testShowFormatFails :: Show t => String -> Format t -> t -> TestTree
testShowFormatFails name fmt val = nameTest (name ++ ": " ++ show val) $ assertEqual "" Nothing $ formatShowM fmt val

testShowFormats :: TestTree
testShowFormats =
nameTest
Expand Down Expand Up @@ -260,6 +266,18 @@
, testReadFormat "timeOffsetFormat" (timeOffsetFormat BasicFormat) "+00" (minutesToTimeZone 0)
, testReadFormat "timeOffsetFormat" (timeOffsetFormat BasicFormat) "-0000" (minutesToTimeZone 0)
, testReadFormat "timeOffsetFormat" (timeOffsetFormat BasicFormat) "-00" (minutesToTimeZone 0)
, testShowReadFormat "timeOffsetFormat" iso8601Format "+24:00" (minutesToTimeZone 1440)
, testReadFormat "timeOffsetFormat" iso8601Format "+24" (minutesToTimeZone 1440)
, testShowReadFormat "timeOffsetFormat" (timeOffsetFormat BasicFormat) "-2400" (minutesToTimeZone (-1440))
, testReadFormat "timeOffsetFormat" (timeOffsetFormat BasicFormat) "-24" (minutesToTimeZone (-1440))
, testReadFormatFails "timeOffsetFormat" (iso8601Format :: Format TimeZone) "+23:60"
, testReadFormatFails "timeOffsetFormat" (iso8601Format :: Format TimeZone) "+24:01"
, testReadFormatFails "timeOffsetFormat" (iso8601Format :: Format TimeZone) "+25:00"
, testReadFormatFails "timeOffsetFormat" (timeOffsetFormat BasicFormat) "-2360"
, testReadFormatFails "timeOffsetFormat" (timeOffsetFormat BasicFormat) "-2401"
, testReadFormatFails "timeOffsetFormat" (timeOffsetFormat BasicFormat) "-2500"
, testShowFormatFails "timeOffsetFormat" (iso8601Format :: Format TimeZone) (minutesToTimeZone 1441)
, testShowFormatFails "timeOffsetFormat" (timeOffsetFormat BasicFormat) (minutesToTimeZone (-1441))
, testShowReadFormat "timeOffsetFormat" iso8601Format "+00:10" (minutesToTimeZone 10)
, testShowReadFormat "timeOffsetFormat" iso8601Format "-00:10" (minutesToTimeZone (-10))
, testShowReadFormat "timeOffsetFormat" iso8601Format "+01:35" (minutesToTimeZone 95)
Expand Down
6 changes: 6 additions & 0 deletions test/main/Test/Format/ParseTime.hs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ particularParseTests =
""
(Just lowerKnownZone)
(parseTimeM False lowerKnownZoneLocale "%Z" "foo")
, parseTest False (Just $ TimeZone 1440 False "") "%z" "+2400"
, parseTest False (Just $ TimeZone 5940 False "") "%z" "+9900"
]
where
lowerAmPmLocale = defaultTimeLocale{amPm = ("am", "pm")}
Expand All @@ -298,6 +300,10 @@ badParseTests =
"bad"
[ parseTest False (Nothing :: Maybe Day) "%Y" ""
, parseTest False (Nothing :: Maybe TimeOfDay) "%-H" "18446744073709551616"
, parseTest False (Nothing :: Maybe TimeZone) "%z" "+2360"
, parseTest False (Nothing :: Maybe TimeZone) "%Ez" "+23:60"
, parseTest False (Nothing :: Maybe TimeZone) "%Z" "+2360"
, parseTest False (Nothing :: Maybe TimeZone) "%EZ" "+23:60"
]

{-
Expand Down
Loading