]> gitweb @ CieloNegro.org - time-http.git/blobdiff - Data/Time/RFC822/Parsec.hs
Data.Time.RFC822 now fully works
[time-http.git] / Data / Time / RFC822 / Parsec.hs
diff --git a/Data/Time/RFC822/Parsec.hs b/Data/Time/RFC822/Parsec.hs
new file mode 100644 (file)
index 0000000..f80633e
--- /dev/null
@@ -0,0 +1,133 @@
+{-# LANGUAGE FlexibleContexts #-}
+module Data.Time.RFC822.Parsec
+    ( parser
+    )
+    where
+
+import Control.Monad
+import Data.Fixed
+import Data.Time
+import Data.Time.Calendar.WeekDate
+import Data.Time.HTTP.Common
+import Text.Parsec
+
+
+parser :: Stream s m Char => ParsecT s u m ZonedTime
+parser = dateTime
+
+
+dateTime :: Stream s m Char => ParsecT s u m ZonedTime
+dateTime = do weekDay <- optionMaybe $
+                         do w <- shortWeekDayNameP
+                            _ <- string ", "
+                            return w
+              gregDay <- date
+              case weekDay of
+                Nothing
+                    -> return () -- No day in week exists.
+                Just givenWD
+                    -> let (_, _, correctWD) = toWeekDate gregDay
+                       in
+                         if correctWD == givenWD then
+                             return () -- Correct day in the week.
+                         else
+                             let (year, month, day) = toGregorian gregDay
+                             in
+                               fail $ concat [ "Gregorian day "
+                                             , show year
+                                             , "-"
+                                             , show month
+                                             , "-"
+                                             , show day
+                                             , " is "
+                                             , longWeekDayName correctWD
+                                             , ", not "
+                                             , longWeekDayName givenWD
+                                             ]
+              (tod, timeZone) <- time
+              return ZonedTime {
+                           zonedTimeToLocalTime = LocalTime {
+                                                    localDay       = gregDay
+                                                  , localTimeOfDay = tod
+                                                  }
+                         , zonedTimeZone        = timeZone
+                         }
+
+date :: Stream s m Char => ParsecT s u m Day
+date = do day   <- read2
+          _     <- char ' '
+          month <- shortMonthNameP
+          _     <- char ' '
+          year  <- liftM (+ 1900) read2
+          _     <- char ' '
+
+          case fromGregorianValid (toInteger year) month day of
+            Nothing
+                -> fail $ concat [ "Invalid gregorian day: "
+                                 , show year
+                                 , "-"
+                                 , show month
+                                 , "-"
+                                 , show day
+                                 ]
+            Just gregDay
+                -> return gregDay
+
+time :: Stream s m Char => ParsecT s u m (TimeOfDay, TimeZone)
+time = do tod <- hour
+          _   <- char ' '
+          tz  <- zone
+          return (tod, tz)
+
+hour :: Stream s m Char => ParsecT s u m TimeOfDay
+hour = do hour   <- read2
+          minute <- char ':' >> read2
+          second <- option 0 (char ':' >> read2)
+          case makeTimeOfDayValid hour minute second of
+            Nothing
+                -> fail $ concat [ "Invalid time of day: "
+                                 , show hour
+                                 , ":"
+                                 , show minute
+                                 , ":"
+                                 , showFixed True second
+                                 ]
+            Just tod
+                -> return tod
+
+zone :: Stream s m Char => ParsecT s u m TimeZone
+zone = choice [ string "UT"  >> return (TimeZone 0 False "UT" )
+              , string "GMT" >> return (TimeZone 0 False "GMT")
+              , char 'E'
+                >> choice [ string "ST" >> return (TimeZone ((-5) * 60) False "EST")
+                          , string "DT" >> return (TimeZone ((-4) * 60) True  "EDT")
+                          ]
+              , char 'C'
+                >> choice [ string "ST" >> return (TimeZone ((-6) * 60) False "CST")
+                          , string "DT" >> return (TimeZone ((-5) * 60) True  "CDT")
+                          ]
+              , char 'M'
+                >> choice [ string "ST" >> return (TimeZone ((-7) * 60) False "MST")
+                          , string "DT" >> return (TimeZone ((-6) * 60) True  "MDT")
+                          , return (TimeZone ((-12) * 60) False "M")
+                          ]
+              , char 'P'
+                >> choice [ string "ST" >> return (TimeZone ((-8) * 60) False "PST")
+                          , string "DT" >> return (TimeZone ((-7) * 60) True  "PDT")
+                          ]
+              , char 'Z' >> return (TimeZone 0           False "Z")
+              , char 'A' >> return (TimeZone ((-1) * 60) False "A")
+              , char 'N' >> return (TimeZone (  1  * 60) False "N")
+              , char 'Y' >> return (TimeZone ( 12  * 60) False "Y")
+              , do sign   <- (char '+' >> return 1)
+                             <|>
+                             (char '-' >> return (-1))
+                   hour   <- read2
+                   minute <- read2
+                   let tz = TimeZone {
+                              timeZoneMinutes    = (sign * (hour * 60 + minute))
+                            , timeZoneSummerOnly = False
+                            , timeZoneName       = timeZoneOffsetString tz
+                            }
+                   return tz
+              ]