{-# LANGUAGE
- OverloadedStrings
+ FlexibleInstances
+ , MultiParamTypeClasses
+ , OverloadedStrings
, UnicodeSyntax
#-}
-- |This module provides functions for ANSI C's asctime() format.
-- As you can see, it has no time zone info. "Data.Time.HTTP" will
-- treat it as UTC.
module Data.Time.Asctime
- ( -- * Formatting
- toAscii
- , toAsciiBuilder
-
- -- * Parsing
- , fromAscii
+ ( Asctime
, asctime
)
where
+import Control.Applicative
import Data.Ascii (Ascii, AsciiBuilder)
import qualified Data.Ascii as A
import Data.Attoparsec.Char8
+import Data.Convertible.Base
import Data.Monoid.Unicode
+import Data.Tagged
import Data.Time
import Data.Time.Calendar.WeekDate
import Data.Time.HTTP.Common
import Prelude.Unicode
--- |Convert a 'LocalTime' to ANSI C's @asctime()@ string.
-toAscii ∷ LocalTime → Ascii
-toAscii = A.fromAsciiBuilder ∘ toAsciiBuilder
+-- |The phantom type for conversion between ANSI C's @asctime()@
+-- string and 'LocalTime'.
+data Asctime
--- |Parse an ANSI C's @asctime()@ string. When the string can't be
--- parsed, it returns @'Left' err@.
-fromAscii ∷ Ascii → Either String LocalTime
-fromAscii = parseOnly p ∘ A.toByteString
- where
- p = do zt ← asctime
- endOfInput
- return zt
+instance ConvertSuccess LocalTime (Tagged Asctime Ascii) where
+ {-# INLINE convertSuccess #-}
+ convertSuccess = (A.fromAsciiBuilder <$>) ∘ cs
+
+instance ConvertAttempt LocalTime (Tagged Asctime Ascii) where
+ {-# INLINE convertAttempt #-}
+ convertAttempt = return ∘ cs
+
+instance ConvertSuccess LocalTime (Tagged Asctime AsciiBuilder) where
+ {-# INLINE convertSuccess #-}
+ convertSuccess = Tagged ∘ toAsciiBuilder
+
+instance ConvertAttempt LocalTime (Tagged Asctime AsciiBuilder) where
+ {-# INLINE convertAttempt #-}
+ convertAttempt = return ∘ cs
+
+instance ConvertAttempt (Tagged Asctime Ascii) LocalTime where
+ {-# INLINE convertAttempt #-}
+ convertAttempt = parseAttempt' asctime ∘ untag
-- |Parse an ANSI C's @asctime()@ string.
asctime ∷ Parser LocalTime
return (LocalTime gregDay tod)
--- |Convert a 'LocalTime' to ANSI C's @asctime()@ string.
toAsciiBuilder ∷ LocalTime → AsciiBuilder
toAsciiBuilder localTime
= let (year, month, day) = toGregorian (localDay localTime)
, assertTimeOfDayIsGood
, optionMaybe
+ , finishOff
+
+ , parseAttempt
+ , parseAttempt'
)
where
import Blaze.ByteString.Builder.ByteString as B
import Blaze.Text.Int as BT
import Control.Applicative
+import Control.Exception.Base
import Control.Monad
-import Data.Ascii (AsciiBuilder)
+import Control.Monad.Unicode
+import Data.Ascii (Ascii, AsciiBuilder)
import qualified Data.Ascii as A
+import Data.Attempt
import Data.Attoparsec.Char8 as P
+import Data.ByteString (ByteString)
import Data.Char
import Data.Monoid.Unicode
import Data.Fixed
(year, month, day) = toGregorian gregDay
in
unless (givenWD ≡ correctWD)
- $ fail
+ ∘ fail
$ concat [ "Gregorian day "
, show year
, "-"
{-# INLINE optionMaybe #-}
optionMaybe p
= option Nothing (Just <$> p)
+
+finishOff ∷ Parser α → Parser α
+{-# INLINE finishOff #-}
+finishOff = ((endOfInput *>) ∘ return =≪)
+
+parseAttempt ∷ Exception e
+ ⇒ (String → e)
+ → Parser α
+ → ByteString
+ → Attempt α
+{-# INLINEABLE parseAttempt #-}
+parseAttempt f p bs
+ = case parseOnly (finishOff p) bs of
+ Right α → Success α
+ Left e → Failure $ f e
+
+parseAttempt' ∷ Parser α → Ascii → Attempt α
+{-# INLINE parseAttempt' #-}
+parseAttempt' p a = parseAttempt h p bs
+ where
+ h ∷ String → StringException
+ h _ = StringException $ A.toString a
+
+ bs ∷ ByteString
+ bs = A.toByteString a
module Main (main) where
import Control.Applicative
import Control.Applicative.Unicode
+import Data.Ascii (Ascii)
+import Data.Convertible.Base
+import Data.Tagged
import Data.Time
-import qualified Data.Time.Asctime as Asctime
+import Data.Time.Asctime
import qualified Data.Time.HTTP as HTTP
import qualified Data.Time.RFC733 as RFC733
import qualified Data.Time.RFC1123 as RFC1123
tests ∷ [Property]
tests = [ -- Asctime
- property ( Asctime.fromAscii "Sun Nov 6 08:49:37 1994"
- ≡ Right referenceLocalTime )
-
- , property ( "Sun Nov 6 08:49:37 1994"
- ≡ Asctime.toAscii referenceLocalTime )
-
- , property $ \lt → Right lt ≡ Asctime.fromAscii (Asctime.toAscii lt)
+ property ( convertUnsafe ( Tagged "Sun Nov 6 08:49:37 1994"
+ ∷ Tagged Asctime Ascii
+ )
+ ≡ referenceLocalTime
+ )
+
+ , property ( ( Tagged "Sun Nov 6 08:49:37 1994"
+ ∷ Tagged Asctime Ascii
+ )
+ ≡ cs referenceLocalTime
+ )
+
+ , property $ \lt → lt ≡ convertUnsafe ( cs (lt ∷ LocalTime)
+ ∷ Tagged Asctime Ascii
+ )
-- RFC733
, property ( RFC733.fromAscii "Sunday, 06-Nov-94 08:49:37 GMT"
-- HTTP
, property $ \ut → Right ut ≡ HTTP.fromAscii (HTTP.toAscii ut )
- , property $ \ut → Right ut ≡ HTTP.fromAscii (Asctime.toAscii (ut2lt ut))
+ , property $ \ut → Right ut ≡ HTTP.fromAscii (untag (cs (ut2lt ut) ∷ Tagged Asctime Ascii))
, property $ \ut → Right ut ≡ HTTP.fromAscii (RFC733.toAscii (ut2zt ut))
, property $ \ut → Right ut ≡ HTTP.fromAscii (RFC1123.toAscii (ut2zt ut))
]
Build-depends:
ascii == 0.0.*,
+ attempt == 0.3.*,
attoparsec == 0.9.*,
- blaze-builder == 0.3.*,
- blaze-textual == 0.2.*,
base == 4.*,
base-unicode-symbols == 0.2.*,
+ blaze-builder == 0.3.*,
+ blaze-textual == 0.2.*,
+ bytestring == 0.9.*,
convertible-text == 0.3.*,
tagged == 0.2.*,
time == 1.2.*
Build-depends:
QuickCheck == 2.4.*,
ascii == 0.0.*,
+ attempt == 0.3.*,
attoparsec == 0.9.*,
- blaze-builder == 0.3.*,
- blaze-textual == 0.2.*,
base == 4.*,
base-unicode-symbols == 0.2.*,
+ blaze-builder == 0.3.*,
+ blaze-textual == 0.2.*,
+ bytestring == 0.9.*,
convertible-text == 0.3.*,
tagged == 0.2.*,
time == 1.2.*