{-# LANGUAGE
- FlexibleInstances
+ FlexibleContexts
+ , FlexibleInstances
, MultiParamTypeClasses
, OverloadedStrings
, TemplateHaskell
)
where
import Control.Applicative
+import Control.Failure
import Data.Ascii (Ascii, AsciiBuilder)
import qualified Data.Ascii as A
import Data.Attoparsec.Char8
import Data.Convertible.Base
+import Data.Convertible.Utils
import Data.Monoid.Unicode
import Data.Tagged
import Data.Time
-- |The phantom type for conversions between RFC 822 date and time
-- strings and 'ZonedTime'.
--
--- >>> convertSuccess (ZonedTime (LocalTime (ModifiedJulianDay 49662) (TimeOfDay 8 49 37)) utc)
--- Tagged "Sun, 06 Nov 94 08:49:37 GMT"
+-- >>> convertAttempt (ZonedTime (LocalTime (ModifiedJulianDay 49662) (TimeOfDay 8 49 37)) utc)
+-- Success (Tagged "Sun, 06 Nov 94 08:49:37 GMT")
+--
+-- Note that RFC 822 has a Y2K problem so converting 'ZonedTime' whose
+-- gregorian year is earlier than 1900 or from 2000 onward results in
+-- @'ConvertBoundsException' 'Day' 'ZonedTime'@.
data RFC822
-instance ConvertSuccess ZonedTime (Tagged RFC822 Ascii) where
- {-# INLINE convertSuccess #-}
- convertSuccess = (A.fromAsciiBuilder <$>) ∘ cs
+instance ConvertAttempt ZonedTime (Tagged RFC822 Ascii) where
+ {-# INLINE convertAttempt #-}
+ convertAttempt = ((A.fromAsciiBuilder <$>) <$>) ∘ ca
-instance ConvertSuccess ZonedTime (Tagged RFC822 AsciiBuilder) where
- {-# INLINE convertSuccess #-}
- convertSuccess = Tagged ∘ toAsciiBuilder
+instance ConvertAttempt ZonedTime (Tagged RFC822 AsciiBuilder) where
+ {-# INLINE convertAttempt #-}
+ convertAttempt = (Tagged <$>) ∘ toAsciiBuilder
instance ConvertSuccess TimeZone (Tagged RFC822 Ascii) where
{-# INLINE convertSuccess #-}
, read4digitsTZ
]
-toAsciiBuilder ∷ ZonedTime → AsciiBuilder
+toAsciiBuilder ∷ Failure (ConvertBoundsException Day ZonedTime) f
+ ⇒ ZonedTime
+ → f AsciiBuilder
toAsciiBuilder zonedTime
= let localTime = zonedTimeToLocalTime zonedTime
timeZone = zonedTimeZone zonedTime
(_, _, week) = toWeekDate (localDay localTime)
timeOfDay = localTimeOfDay localTime
in
- shortWeekDayName week
- ⊕ A.toAsciiBuilder ", "
- ⊕ show2 day
- ⊕ A.toAsciiBuilder " "
- ⊕ shortMonthName month
- ⊕ A.toAsciiBuilder " "
- ⊕ show2 (year `mod` 100)
- ⊕ A.toAsciiBuilder " "
- ⊕ show2 (todHour timeOfDay)
- ⊕ A.toAsciiBuilder ":"
- ⊕ show2 (todMin timeOfDay)
- ⊕ A.toAsciiBuilder ":"
- ⊕ show2 (floor (todSec timeOfDay) ∷ Int)
- ⊕ A.toAsciiBuilder " "
- ⊕ untag (cs timeZone ∷ Tagged RFC822 AsciiBuilder)
+ if year < 1900 ∨ year ≥ 2000 then
+ let minDay = fromGregorian 1900 1 1
+ maxDay = fromGregorian 1999 12 31
+ in
+ failure $ ConvertBoundsException minDay maxDay zonedTime
+ else
+ return $
+ shortWeekDayName week
+ ⊕ A.toAsciiBuilder ", "
+ ⊕ show2 day
+ ⊕ A.toAsciiBuilder " "
+ ⊕ shortMonthName month
+ ⊕ A.toAsciiBuilder " "
+ ⊕ show2 (year `mod` 100)
+ ⊕ A.toAsciiBuilder " "
+ ⊕ show2 (todHour timeOfDay)
+ ⊕ A.toAsciiBuilder ":"
+ ⊕ show2 (todMin timeOfDay)
+ ⊕ A.toAsciiBuilder ":"
+ ⊕ show2 (floor (todSec timeOfDay) ∷ Int)
+ ⊕ A.toAsciiBuilder " "
+ ⊕ untag (cs timeZone ∷ Tagged RFC822 AsciiBuilder)
-deriveAttempts [ ([t| ZonedTime |], [t| Tagged RFC822 Ascii |])
- , ([t| ZonedTime |], [t| Tagged RFC822 AsciiBuilder |])
- , ([t| TimeZone |], [t| Tagged RFC822 Ascii |])
+deriveAttempts [ ([t| TimeZone |], [t| Tagged RFC822 Ascii |])
, ([t| TimeZone |], [t| Tagged RFC822 AsciiBuilder |])
]
≡ Just referenceZonedTime
)
- , property ( (Tagged "Sun, 06 Nov 94 08:49:37 GMT" ∷ Tagged RFC822 Ascii)
- ≡ cs referenceZonedTime
+ , property ( Just (Tagged "Sun, 06 Nov 94 08:49:37 GMT" ∷ Tagged RFC822 Ascii)
+ ≡ fromAttempt (ca referenceZonedTime)
)
- , property $ \zt → Just (untag zt) ≡ fromAttempt (ca (cs (untag (zt ∷ Tagged Cent20 ZonedTime))
- ∷ Tagged RFC822 Ascii))
+ , property $ \zt → let zt' = do a ← ca $ untag (zt ∷ Tagged Cent20 ZonedTime)
+ ca (a ∷ Tagged RFC822 Ascii)
+ in
+ fromAttempt zt' ≡ Just (untag zt)
-- RFC1123
, property ( fromAttempt (ca (Tagged "Sun, 06 Nov 1994 08:49:37 GMT" ∷ Tagged RFC1123 Ascii))
, property $ \ut → Just ut ≡ fromAttempt (ca (cs ut ∷ Tagged HTTP Ascii) ∷ Attempt UTCTime)
, property $ \ut → Just ut ≡ fromAttempt (ca (retagHTTP (cs (ut2lt ut) ∷ Tagged C Ascii)))
, property $ \ut → Just ut ≡ fromAttempt (ca (retagHTTP (cs (ut2zt ut) ∷ Tagged RFC733 Ascii)))
- , property $ \ut → Just (untag ut) ≡ fromAttempt (ca (retagHTTP (cs (ut2zt (untag (ut ∷ Tagged Cent20 UTCTime)))
- ∷ Tagged RFC822 Ascii)))
+ , property $ \ut → let zt = ut2zt $ untag (ut ∷ Tagged Cent20 UTCTime)
+ ut' = do a ← ca zt
+ ca $ retagHTTP (a ∷ Tagged RFC822 Ascii)
+ in
+ fromAttempt ut' ≡ Just (untag ut)
, property $ \ut → Just ut ≡ fromAttempt (ca (retagHTTP (cs (ut2zt ut) ∷ Tagged RFC1123 Ascii)))
]
where
--- /dev/null
+--- !ditz.rubyforge.org,2008-03-06/issue
+title: Don't forget that conversion from ZonedTime to RFC-822 date and time can fail, due to its Y2K problem.
+desc: Make it ConvertAttempt, not ConvertSuccess.
+type: :bugfix
+component: time-http
+release: time-http-0.4
+reporter: PHO <pho@cielonegro.org>
+status: :closed
+disposition: :fixed
+creation_time: 2011-12-16 13:11:18.237158 Z
+references: []
+
+id: d8873d7c8927894257a438b5f02752d3ba702f66
+log_events:
+- - 2011-12-16 13:11:24.381526 Z
+ - PHO <pho@cielonegro.org>
+ - created
+ - Done.
+- - 2011-12-16 13:11:53.887803 Z
+ - PHO <pho@cielonegro.org>
+ - assigned to release time-http-0.4 from unassigned
+ - ""
+- - 2011-12-16 13:12:10.385138 Z
+ - PHO <pho@cielonegro.org>
+ - closed with disposition fixed
+ - ""
+git_branch: