]> gitweb @ CieloNegro.org - time-http.git/commitdiff
Data.Time.RFC733 now fully works
authorPHO <pho@cielonegro.org>
Wed, 17 Mar 2010 06:24:36 +0000 (15:24 +0900)
committerPHO <pho@cielonegro.org>
Wed, 17 Mar 2010 06:24:36 +0000 (15:24 +0900)
Data/Time/HTTP/Common.hs
Data/Time/RFC733.hs [new file with mode: 0644]
Data/Time/RFC733/Parsec.hs [new file with mode: 0644]
Data/Time/RFC822.hs
Data/Time/RFC822/Parsec.hs
time-http.cabal

index 1ea61de90bc2a21cc0e85a5d41fe63225d188a34..6cb59b3bc63aaf06b1701c0ba978dc80870d3c4c 100644 (file)
@@ -9,15 +9,28 @@ module Data.Time.HTTP.Common
     , shortMonthName
     , shortMonthNameP
 
+    , longMonthName
+    , longMonthNameP
+
     , show2
     , show4
 
     , read2
     , read4
+
+    , showTZ
+    , read4digitsTZ
+
+    , assertWeekDayIsGood
+    , assertGregorianDateIsGood
+    , assertTimeOfDayIsGood
     )
     where
 
 import Control.Monad
+import Data.Fixed
+import Data.Time
+import Data.Time.Calendar.WeekDate
 import Text.Parsec
 
 shortWeekDayName :: Int -> String
@@ -108,6 +121,45 @@ shortMonthNameP
              , string "Dec" >> return 12
              ]
 
+longMonthName :: Int -> String
+longMonthName  1 = "January"
+longMonthName  2 = "February"
+longMonthName  3 = "March"
+longMonthName  4 = "April"
+longMonthName  5 = "May"
+longMonthName  6 = "June"
+longMonthName  7 = "July"
+longMonthName  8 = "August"
+longMonthName  9 = "September"
+longMonthName 10 = "October"
+longMonthName 11 = "November"
+longMonthName 12 = "December"
+longMonthName  n = error ("longMonthName: unknown month number: " ++ show n)
+
+longMonthNameP :: Stream s m Char => ParsecT s u m Int
+longMonthNameP
+    = choice [ char 'J'
+               >> choice [ string "anuary" >> return 1
+                         , char 'u'
+                           >> choice [ string "ne" >> return 6
+                                     , string "ly" >> return 7
+                                     ]
+                         ]
+             , string "February" >> return 2
+             , string "Ma"
+               >> choice [ string "rch" >> return 3
+                         , char 'y' >> return 5
+                         ]
+             , char 'A'
+               >> choice [ string "pril" >> return 4
+                         , string "ugust" >> return 8
+                         ]
+             , string "September" >> return 9
+             , string "October"   >> return 10
+             , string "November"  >> return 11
+             , string "December"  >> return 12
+             ]
+
 show4 :: Integral i => i -> String
 show4 i
     | i >= 0 && i < 10    = "000" ++ show i
@@ -149,3 +201,76 @@ fromC '7' = 7
 fromC '8' = 8
 fromC '9' = 9
 fromC _   = undefined
+
+showTZ :: TimeZone -> String
+showTZ tz
+    = case timeZoneMinutes tz of
+        offset | offset <  0 -> '-' : showTZ' (negate offset)
+               | otherwise   -> '+' : showTZ' offset
+    where
+      showTZ' offset
+          = let h = offset `div` 60
+                m = offset - h * 60
+            in
+              concat [show2 h, show2 m]
+
+read4digitsTZ :: Stream s m Char => ParsecT s u m TimeZone
+read4digitsTZ
+    = 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
+
+assertWeekDayIsGood :: Stream s m t => Int -> Day -> ParsecT s u m ()
+assertWeekDayIsGood givenWD gregDay
+    = let (_, _, correctWD ) = toWeekDate  gregDay
+          (year, month, day) = toGregorian gregDay
+      in
+        unless (givenWD == correctWD)
+                   $ fail
+                   $ concat [ "Gregorian day "
+                            , show year
+                            , "-"
+                            , show month
+                            , "-"
+                            , show day
+                            , " is "
+                            , longWeekDayName correctWD
+                            , ", not "
+                            , longWeekDayName givenWD
+                            ]
+
+assertGregorianDateIsGood :: Stream s m t => Integer -> Int -> Int -> ParsecT s u m Day
+assertGregorianDateIsGood year month day
+    = case fromGregorianValid year month day of
+        Nothing
+            -> fail $ concat [ "Invalid gregorian day: "
+                             , show year
+                             , "-"
+                             , show month
+                             , "-"
+                             , show day
+                             ]
+        Just gregDay
+            -> return gregDay
+
+assertTimeOfDayIsGood :: Stream s m t => Int -> Int -> Pico -> ParsecT s u m TimeOfDay
+assertTimeOfDayIsGood hour minute second
+    = case makeTimeOfDayValid hour minute second of
+        Nothing
+            -> fail $ concat [ "Invalid time of day: "
+                             , show hour
+                             , ":"
+                             , show minute
+                             , ":"
+                             , showFixed True second
+                             ]
+        Just tod
+            -> return tod
diff --git a/Data/Time/RFC733.hs b/Data/Time/RFC733.hs
new file mode 100644 (file)
index 0000000..a5e28d3
--- /dev/null
@@ -0,0 +1,88 @@
+module Data.Time.RFC733
+    ( format
+    , parse
+    )
+    where
+
+import qualified Text.Parsec as P
+
+import Data.Time
+import Data.Time.Calendar.WeekDate
+import Data.Time.HTTP.Common
+import Data.Time.RFC733.Parsec
+
+{-
+date-time   =  [ day-of-week "," ] date time
+
+day-of-week =  "Monday"    / "Mon"  / "Tuesday"   / "Tue"
+            /  "Wednesday" / "Wed"  / "Thursday"  / "Thu"
+            /  "Friday"    / "Fri"  / "Saturday"  / "Sat"
+            /  "Sunday"    / "Sun"
+
+date        =  1*2DIGIT ["-"] month         ; day month year
+               ["-"] (2DIGIT /4DIGIT)       ;  e.g. 20 Aug [19]77
+
+month       =  "January"   / "Jan"  / "February"  / "Feb"
+            /  "March"     / "Mar"  / "April"     / "Apr"
+            /  "May"                / "June"      / "Jun"
+            /  "July"      / "Jul"  / "August"    / "Aug"
+            /  "September" / "Sep"  / "October"   / "Oct"
+            /  "November"  / "Nov"  / "December"  / "Dec"
+
+time        =  hour zone                    ; ANSI and Military
+                                            ;  (seconds optional)
+
+hour        =  2DIGIT [":"] 2DIGIT [ [":"] 2DIGIT ]
+                                            ; 0000[00] - 2359[59]
+
+zone        = ( ["-"] ( "GMT"               ; Relative to GMT:
+                                            ; North American
+                 /  "NST" /                 ;  Newfoundland:-3:30
+                 /  "AST" / "ADT"           ;  Atlantic: - 4/ - 3
+                 /  "EST" / "EDT"           ;  Eastern:  - 5/ - 4
+                 /  "CST" / "CDT"           ;  Central:  - 6/ - 5
+                 /  "MST" / "MDT"           ;  Mountain: - 7/ - 6
+                 /  "PST" / "PDT"           ;  Pacific:  - 8/ - 7
+                 /  "YST" / "YDT"           ;  Yukon:    - 9/ - 8
+                 /  "HST" / "HDT"           ;  Haw/Ala   -10/ - 9
+                 /  "BST" / "BDT"           ;  Bering:   -11/ -10
+                    1ALPHA       ))         ; Military: Z = GMT;
+                                            ;  A:-1; (J not used)
+                                            ;  M:-12; N:+1; Y:+12
+            / ( ("+" / "-") 4DIGIT )        ; Local differential
+                                            ;  hours/min. (HHMM)
+-}
+
+format :: ZonedTime -> String
+format zonedTime
+    = let localTime          = zonedTimeToLocalTime zonedTime
+          timeZone           = zonedTimeZone zonedTime
+          (year, month, day) = toGregorian (localDay localTime)
+          (_, _, week)       = toWeekDate  (localDay localTime)
+          timeOfDay          = localTimeOfDay localTime
+      in
+        concat [ longWeekDayName week
+               , ", "
+               , show2 day
+               , "-"
+               , shortMonthName month
+               , "-"
+               , show4 year
+               , " "
+               , show2 (todHour timeOfDay)
+               , ":"
+               , show2 (todMin timeOfDay)
+               , ":"
+               , show2 (floor (todSec timeOfDay))
+               , " "
+               , showTZ timeZone
+               ]
+
+parse :: String -> Maybe ZonedTime
+parse src = case P.parse p "" src of
+              Right zt -> Just zt
+              Left  _  -> Nothing
+    where
+      p = do zt <- parser
+             _  <- P.eof
+             return zt
diff --git a/Data/Time/RFC733/Parsec.hs b/Data/Time/RFC733/Parsec.hs
new file mode 100644 (file)
index 0000000..baf2469
--- /dev/null
@@ -0,0 +1,107 @@
+{-# LANGUAGE FlexibleContexts #-}
+module Data.Time.RFC733.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 <- try longWeekDayNameP
+                                 <|>
+                                 shortWeekDayNameP
+                            _ <- string ", "
+                            return w
+              gregDay <- date
+              case weekDay of
+                Nothing
+                    -> return ()
+                Just givenWD
+                    -> assertWeekDayIsGood givenWD gregDay
+              (tod, timeZone) <- time
+              let lt = LocalTime gregDay tod
+                  zt = ZonedTime lt timeZone
+              return zt
+
+date :: Stream s m Char => ParsecT s u m Day
+date = do day   <- read2
+          _     <- char '-' <|> char ' '
+          month <- try longMonthNameP
+                   <|>
+                   shortMonthNameP
+          _     <- char '-' <|> char ' '
+          year  <- try read4
+                   <|>
+                   liftM (+ 1900) read2
+          _     <- char ' '
+          assertGregorianDateIsGood (toInteger year) month day
+
+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
+          _      <- optional (char ':')
+          minute <- read2
+          second <- option 0 $
+                    do _ <- optional (char ':')
+                       read2
+          assertTimeOfDayIsGood hour minute second
+
+zone :: Stream s m Char => ParsecT s u m TimeZone
+zone = choice [ string "GMT" >> return (TimeZone 0 False "GMT")
+              , char 'N'
+                >> choice [ string "ST" >> return (TimeZone ((-3) * 60 - 30) False "NST")
+                          , return (TimeZone (1 * 60) False "N")
+                          ]
+              , char 'A'
+                >> choice [ string "ST" >> return (TimeZone ((-4) * 60) False "AST")
+                          , string "DT" >> return (TimeZone ((-3) * 60) False "AST")
+                          , return (TimeZone ((-1) * 60) False "A")
+                          ]
+              , 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 'Y'
+                >> choice [ string "ST" >> return (TimeZone ((-9) * 60) False "YST")
+                          , string "DT" >> return (TimeZone ((-8) * 60) True  "YDT")
+                          , return (TimeZone ( 12  * 60) False "Y")
+                          ]
+              , char 'H'
+                >> choice [ string "ST" >> return (TimeZone ((-10) * 60) False "HST")
+                          , string "DT" >> return (TimeZone (( -9) * 60) True  "HDT")
+                          ]
+              , char 'B'
+                >> choice [ string "ST" >> return (TimeZone ((-11) * 60) False "BST")
+                          , string "DT" >> return (TimeZone ((-10) * 60) True  "BDT")
+                          ]
+              , char 'Z' >> return (TimeZone 0 False "Z")
+              , read4digitsTZ
+              ]
index 4b3d91cf8799714e45eb15c1dfe16e5b77c72d22..fa4f8efe7d532046dae63901884f54110f43751a 100644 (file)
@@ -69,18 +69,6 @@ format zonedTime
                , showTZ timeZone
                ]
 
-showTZ :: TimeZone -> String
-showTZ tz
-    = case timeZoneMinutes tz of
-        offset | offset <  0 -> '-' : showTZ' (negate offset)
-               | otherwise   -> '+' : showTZ' offset
-    where
-      showTZ' offset
-          = let h = offset `div` 60
-                m = offset - h * 60
-            in
-              concat [show2 h, show2 m]
-
 parse :: String -> Maybe ZonedTime
 parse src = case P.parse p "" src of
               Right zt -> Just zt
index f80633efb5fbd60987f14fc097252d0671fb1795..138b9a4390232f5aadf53af2baf8c923b749c31f 100644 (file)
@@ -15,7 +15,6 @@ 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
@@ -26,32 +25,11 @@ dateTime = do weekDay <- optionMaybe $
                 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
-                                             ]
+                    -> assertWeekDayIsGood givenWD gregDay
               (tod, timeZone) <- time
-              return ZonedTime {
-                           zonedTimeToLocalTime = LocalTime {
-                                                    localDay       = gregDay
-                                                  , localTimeOfDay = tod
-                                                  }
-                         , zonedTimeZone        = timeZone
-                         }
+              let lt = LocalTime gregDay tod
+                  zt = ZonedTime lt timeZone
+              return zt
 
 date :: Stream s m Char => ParsecT s u m Day
 date = do day   <- read2
@@ -60,18 +38,7 @@ date = do day   <- read2
           _     <- 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
+          assertGregorianDateIsGood (toInteger year) month day
 
 time :: Stream s m Char => ParsecT s u m (TimeOfDay, TimeZone)
 time = do tod <- hour
@@ -83,17 +50,7 @@ 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
+          assertTimeOfDayIsGood hour minute second
 
 zone :: Stream s m Char => ParsecT s u m TimeZone
 zone = choice [ string "UT"  >> return (TimeZone 0 False "UT" )
@@ -119,15 +76,5 @@ zone = choice [ string "UT"  >> return (TimeZone 0 False "UT" )
               , 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
+              , read4digitsTZ
               ]
index 6ec5f3d30db71f64bca6552f6d030e60187c4778..cfdc663a1bd485079e378ff34613494ecda3fa91 100644 (file)
@@ -23,6 +23,8 @@ Library
     Exposed-modules:
         Data.Time.RFC822
         Data.Time.RFC822.Parsec
+        Data.Time.RFC733
+        Data.Time.RFC733.Parsec
 
     Other-modules:
         Data.Time.HTTP.Common