3 , MultiParamTypeClasses
8 module Data.Time.Format.RFC822.Internal
14 import Control.Applicative
15 import Data.Ascii (Ascii, AsciiBuilder)
16 import qualified Data.Ascii as A
17 import Data.Attoparsec.Char8
18 import Data.Convertible.Base
19 import Data.Monoid.Unicode
22 import Data.Time.Calendar.WeekDate
23 import Data.Time.Format.HTTP.Common
24 import Prelude.Unicode
26 -- |The phantom type for conversions between RFC 822 date and time
27 -- strings and 'ZonedTime'.
29 -- >>> convertSuccess (ZonedTime (LocalTime (ModifiedJulianDay 49662) (TimeOfDay 8 49 37)) utc)
30 -- Tagged "Sun, 06 Nov 94 08:49:37 GMT"
33 instance ConvertSuccess ZonedTime (Tagged RFC822 Ascii) where
34 {-# INLINE convertSuccess #-}
35 convertSuccess = (A.fromAsciiBuilder <$>) ∘ cs
37 instance ConvertSuccess ZonedTime (Tagged RFC822 AsciiBuilder) where
38 {-# INLINE convertSuccess #-}
39 convertSuccess = Tagged ∘ toAsciiBuilder
41 instance ConvertSuccess TimeZone (Tagged RFC822 Ascii) where
42 {-# INLINE convertSuccess #-}
43 convertSuccess = (A.fromAsciiBuilder <$>) ∘ cs
45 instance ConvertSuccess TimeZone (Tagged RFC822 AsciiBuilder) where
46 {-# INLINE convertSuccess #-}
48 | timeZoneMinutes tz ≡ 0 = Tagged $ A.toAsciiBuilder "GMT"
49 | otherwise = Tagged $ show4digitsTZ tz
51 instance ConvertAttempt (Tagged RFC822 Ascii) ZonedTime where
52 {-# INLINE convertAttempt #-}
53 convertAttempt = parseAttempt' rfc822DateAndTime ∘ untag
55 -- |Parse an RFC 822 date and time string.
56 rfc822DateAndTime ∷ Parser ZonedTime
57 rfc822DateAndTime = dateTime
59 dateTime ∷ Parser ZonedTime
60 dateTime = do weekDay ← optionMaybe $
61 do w ← shortWeekDayNameP
69 -> assertWeekDayIsGood givenWD gregDay
70 (tod, timeZone) ← rfc822Time
71 let lt = LocalTime gregDay tod
72 zt = ZonedTime lt timeZone
78 month ← shortMonthNameP
80 year ← (+ 1900) <$> read2
82 assertGregorianDateIsGood year month day
84 rfc822Time ∷ Parser (TimeOfDay, TimeZone)
85 rfc822Time = do tod ← hms
90 hms ∷ Parser TimeOfDay
92 minute ← char ':' *> read2
93 second ← option 0 (char ':' *> read2)
94 assertTimeOfDayIsGood hour minute second
96 zone ∷ Parser TimeZone
97 zone = choice [ string "UT" *> return (TimeZone 0 False "UT" )
98 , string "GMT" *> return (TimeZone 0 False "GMT")
100 *> choice [ string "ST" *> return (TimeZone ((-5) * 60) False "EST")
101 , string "DT" *> return (TimeZone ((-4) * 60) True "EDT")
104 *> choice [ string "ST" *> return (TimeZone ((-6) * 60) False "CST")
105 , string "DT" *> return (TimeZone ((-5) * 60) True "CDT")
108 *> choice [ string "ST" *> return (TimeZone ((-7) * 60) False "MST")
109 , string "DT" *> return (TimeZone ((-6) * 60) True "MDT")
110 , return (TimeZone ((-12) * 60) False "M")
113 *> choice [ string "ST" *> return (TimeZone ((-8) * 60) False "PST")
114 , string "DT" *> return (TimeZone ((-7) * 60) True "PDT")
116 , char 'Z' *> return (TimeZone 0 False "Z")
117 , char 'A' *> return (TimeZone ((-1) * 60) False "A")
118 , char 'N' *> return (TimeZone ( 1 * 60) False "N")
119 , char 'Y' *> return (TimeZone ( 12 * 60) False "Y")
123 toAsciiBuilder ∷ ZonedTime → AsciiBuilder
124 toAsciiBuilder zonedTime
125 = let localTime = zonedTimeToLocalTime zonedTime
126 timeZone = zonedTimeZone zonedTime
127 (year, month, day) = toGregorian (localDay localTime)
128 (_, _, week) = toWeekDate (localDay localTime)
129 timeOfDay = localTimeOfDay localTime
131 shortWeekDayName week
132 ⊕ A.toAsciiBuilder ", "
134 ⊕ A.toAsciiBuilder " "
135 ⊕ shortMonthName month
136 ⊕ A.toAsciiBuilder " "
137 ⊕ show2 (year `mod` 100)
138 ⊕ A.toAsciiBuilder " "
139 ⊕ show2 (todHour timeOfDay)
140 ⊕ A.toAsciiBuilder ":"
141 ⊕ show2 (todMin timeOfDay)
142 ⊕ A.toAsciiBuilder ":"
143 ⊕ show2 (floor (todSec timeOfDay) ∷ Int)
144 ⊕ A.toAsciiBuilder " "
145 ⊕ untag (cs timeZone ∷ Tagged RFC822 AsciiBuilder)
147 deriveAttempts [ ([t| ZonedTime |], [t| Tagged RFC822 Ascii |])
148 , ([t| ZonedTime |], [t| Tagged RFC822 AsciiBuilder |])
149 , ([t| TimeZone |], [t| Tagged RFC822 Ascii |])
150 , ([t| TimeZone |], [t| Tagged RFC822 AsciiBuilder |])