{-# LANGUAGE FlexibleInstances , MultiParamTypeClasses , OverloadedStrings , TemplateHaskell , TypeSynonymInstances , UnicodeSyntax #-} -- |This module provides functions to parse and format HTTP\/1.1 date -- and time strings -- (<http://tools.ietf.org/html/rfc2616#section-3.3>). -- -- The HTTP\/1.1 specification (RFC 2616) says that HTTP\/1.1 clients -- and servers which parse the date value MUST accept all the -- following formats, though they MUST only generate the RFC 1123 -- format for representing HTTP-date values in header fields: -- -- > Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 -- > Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 -- > Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format -- -- It also says that all HTTP date\/time stamps MUST be represented in -- Greenwich Mean Time (GMT), without exception. For the purposes of -- HTTP, GMT is exactly equal to UTC (Coordinated Universal -- Time). This is indicated in the first two formats by the inclusion -- of @\"GMT\"@ as the three-letter abbreviation for time zone, and -- MUST be assumed when reading the asctime format. -- -- > HTTP-date = rfc1123-date | rfc850-date | asctime-date -- > rfc1123-date = wkday "," SP date1 SP time SP "GMT" -- > rfc850-date = weekday "," SP date2 SP time SP "GMT" -- > asctime-date = wkday SP date3 SP time SP 4DIGIT -- > date1 = 2DIGIT SP month SP 4DIGIT -- > ; day month year (e.g., 02 Jun 1982) -- > date2 = 2DIGIT "-" month "-" 2DIGIT -- > ; day-month-year (e.g., 02-Jun-82) -- > date3 = month SP ( 2DIGIT | ( SP 1DIGIT )) -- > ; month day (e.g., Jun 2) -- > time = 2DIGIT ":" 2DIGIT ":" 2DIGIT -- > ; 00:00:00 - 23:59:59 -- > wkday = "Mon" | "Tue" | "Wed" -- > | "Thu" | "Fri" | "Sat" | "Sun" -- > weekday = "Monday" | "Tuesday" | "Wednesday" -- > | "Thursday" | "Friday" | "Saturday" | "Sunday" -- > month = "Jan" | "Feb" | "Mar" | "Apr" -- > | "May" | "Jun" | "Jul" | "Aug" -- > | "Sep" | "Oct" | "Nov" | "Dec" module Data.Time.Format.HTTP ( HTTP ) 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.Default import Data.Tagged import Data.Time import Data.Time.Format.C import Data.Time.Format.HTTP.Common import Data.Time.Format.RFC733 import Data.Time.Format.RFC822 import Data.Time.Format.RFC1123 import Prelude.Unicode -- |The phantom type for conversions between HTTP/1.1 date and time -- strings and 'UTCTime'. -- -- >>> convertSuccess (Tagged (UTCTime (ModifiedJulianDay 49662) 31777) :: Tagged HTTP UTCTime) -- "Sun, 06 Nov 1994 08:49:37 GMT" data HTTP instance ConvertSuccess (Tagged HTTP UTCTime) Ascii where {-# INLINE convertSuccess #-} convertSuccess = A.fromAsciiBuilder ∘ cs instance ConvertSuccess (Tagged HTTP UTCTime) AsciiBuilder where {-# INLINE convertSuccess #-} convertSuccess = toAsciiBuilder instance ConvertAttempt Ascii (Tagged HTTP UTCTime) where {-# INLINE convertAttempt #-} convertAttempt = parseAttempt' def -- |Parse a date and time string in any of RFC 822, RFC 1123, RFC 850 -- and ANSI C's asctime() formats. -- -- This parser is even more permissive than what HTTP\/1.1 (RFC 2616) -- specifies. That is, it accepts 2-digit years in RFC 822, omitted -- separator symbols in RFC 850, omitted sec fields, and non-GMT time -- zones. I believe this behavior will not cause a problem though. instance Default (Parser (Tagged HTTP UTCTime)) where {-# INLINEABLE def #-} def = Tagged <$> choice [ (zonedTimeToUTC ∘ untag) <$> try (def ∷ Parser (Tagged RFC1123 ZonedTime)) , (zonedTimeToUTC ∘ untag) <$> try (def ∷ Parser (Tagged RFC733 ZonedTime)) , (zonedTimeToUTC ∘ untag) <$> try (def ∷ Parser (Tagged RFC822 ZonedTime)) , (localTimeToUTC utc ∘ untag) <$> (def ∷ Parser (Tagged C LocalTime)) ] toAsciiBuilder ∷ Tagged HTTP UTCTime → AsciiBuilder {-# INLINEABLE toAsciiBuilder #-} toAsciiBuilder = cs ∘ (ut2zt <$>) ∘ retag' where ut2zt ∷ UTCTime → ZonedTime {-# INLINE ut2zt #-} ut2zt = utcToZonedTime gmt gmt ∷ TimeZone {-# INLINE CONLIKE gmt #-} gmt = TimeZone 0 False "GMT" retag' ∷ Tagged τ α → Tagged RFC1123 α {-# INLINE retag' #-} retag' = retag deriveAttempts [ ([t| Tagged HTTP UTCTime |], [t| Ascii |]) , ([t| Tagged HTTP UTCTime |], [t| AsciiBuilder |]) ]