5 -- |Internal functions for "Data.Time.RFC822".
6 module Data.Time.RFC822.Internal
13 import Control.Applicative
14 import Data.Ascii (AsciiBuilder)
15 import qualified Data.Ascii as A
16 import Data.Attoparsec.Char8
17 import Data.Monoid.Unicode
19 import Data.Time.Calendar.WeekDate
20 import Data.Time.HTTP.Common
21 import Prelude.Unicode
23 -- |Parse an RFC 822 date and time string.
24 rfc822DateAndTime ∷ Parser ZonedTime
25 rfc822DateAndTime = dateTime
27 dateTime ∷ Parser ZonedTime
28 dateTime = do weekDay ← optionMaybe $
29 do w ← shortWeekDayNameP
37 -> assertWeekDayIsGood givenWD gregDay
38 (tod, timeZone) ← rfc822time
39 let lt = LocalTime gregDay tod
40 zt = ZonedTime lt timeZone
46 month ← shortMonthNameP
48 year ← (+ 1900) <$> read2
50 assertGregorianDateIsGood year month day
52 -- |Parse the time and time zone of an RFC 822 date and time string.
53 rfc822time ∷ Parser (TimeOfDay, TimeZone)
54 rfc822time = do tod ← hms
59 hms ∷ Parser TimeOfDay
61 minute ← char ':' *> read2
62 second ← option 0 (char ':' *> read2)
63 assertTimeOfDayIsGood hour minute second
65 zone ∷ Parser TimeZone
66 zone = choice [ string "UT" *> return (TimeZone 0 False "UT" )
67 , string "GMT" *> return (TimeZone 0 False "GMT")
69 *> choice [ string "ST" *> return (TimeZone ((-5) * 60) False "EST")
70 , string "DT" *> return (TimeZone ((-4) * 60) True "EDT")
73 *> choice [ string "ST" *> return (TimeZone ((-6) * 60) False "CST")
74 , string "DT" *> return (TimeZone ((-5) * 60) True "CDT")
77 *> choice [ string "ST" *> return (TimeZone ((-7) * 60) False "MST")
78 , string "DT" *> return (TimeZone ((-6) * 60) True "MDT")
79 , return (TimeZone ((-12) * 60) False "M")
82 *> choice [ string "ST" *> return (TimeZone ((-8) * 60) False "PST")
83 , string "DT" *> return (TimeZone ((-7) * 60) True "PDT")
85 , char 'Z' *> return (TimeZone 0 False "Z")
86 , char 'A' *> return (TimeZone ((-1) * 60) False "A")
87 , char 'N' *> return (TimeZone ( 1 * 60) False "N")
88 , char 'Y' *> return (TimeZone ( 12 * 60) False "Y")
92 -- |No need to explain.
93 showRFC822TimeZone ∷ TimeZone → AsciiBuilder
95 | timeZoneMinutes tz ≡ 0 = A.toAsciiBuilder "GMT"
96 | otherwise = show4digitsTZ tz
98 -- |Convert a 'ZonedTime' to RFC 822 date and time string.
99 toAsciiBuilder ∷ ZonedTime → AsciiBuilder
100 toAsciiBuilder zonedTime
101 = let localTime = zonedTimeToLocalTime zonedTime
102 timeZone = zonedTimeZone zonedTime
103 (year, month, day) = toGregorian (localDay localTime)
104 (_, _, week) = toWeekDate (localDay localTime)
105 timeOfDay = localTimeOfDay localTime
107 shortWeekDayName week
108 ⊕ A.toAsciiBuilder ", "
110 ⊕ A.toAsciiBuilder " "
111 ⊕ shortMonthName month
112 ⊕ A.toAsciiBuilder " "
113 ⊕ show2 (year `mod` 100)
114 ⊕ A.toAsciiBuilder " "
115 ⊕ show2 (todHour timeOfDay)
116 ⊕ A.toAsciiBuilder ":"
117 ⊕ show2 (todMin timeOfDay)
118 ⊕ A.toAsciiBuilder ":"
119 ⊕ show2 (floor (todSec timeOfDay) ∷ Int)
120 ⊕ A.toAsciiBuilder " "
121 ⊕ showRFC822TimeZone timeZone