]> gitweb.michael.orlitzky.com - dead/htsn-import.git/blobdiff - src/TSN/Picklers.hs
Add a new (un)pickler for the auto racing schedule "TBA"-able time field.
[dead/htsn-import.git] / src / TSN / Picklers.hs
index 16a25ef2f6043811fa9c299daad8132b51dbf81a..c70b2ba84edc5d627575622d1bb2a609f99bb195 100644 (file)
@@ -3,16 +3,19 @@
 --
 module TSN.Picklers (
   xp_date,
-  xp_team_id )
+  xp_gamedate,
+  xp_tba_time,
+  xp_time,
+  xp_time_stamp )
 where
 
-import Data.Time.Clock ( UTCTime )
+-- System imports.
+import Data.Time.Clock ( NominalDiffTime, UTCTime, addUTCTime )
 import Data.Time.Format ( formatTime, parseTime )
 import System.Locale ( defaultTimeLocale )
-import Text.Printf ( printf )
-import Text.Read ( readMaybe )
 import Text.XML.HXT.Arrow.Pickle (
   xpText,
+  xpWrap,
   xpWrapMaybe )
 import Text.XML.HXT.Arrow.Pickle.Xml ( PU )
 
@@ -32,21 +35,116 @@ xp_date =
     from_date = formatTime defaultTimeLocale format
 
 
--- | Parse a team_id. This *should* just be an 'Int', but TSN is doing
---   something weird. First of all, player IDs do look like normal
---   'Int's. But the team IDs are all stuck in the triple digits, and
---   double-digit team IDs appear to be padded to three characters
---   with a leading '0'. So maybe they're treating these as text?
+-- | (Un)pickle a UTCTime from a weather forecast's gamedate. Example
+--   input looks like,
 --
---   In any case, we do the simplest thing that is correct for all the
---   XML we've got: pad it to (only) three digits on pickling.
+--   \<forecast gamedate=\"Monday, December 30th\"\>
 --
-xp_team_id :: PU Int
-xp_team_id =
-  (to_team_id, from_team_id) `xpWrapMaybe` xpText
+--   When unpickling we get rid of the suffixes \"st\", \"nd\", \"rd\", and
+--   \"th\". During pickling, we add them back based on the last digit
+--   of the date.
+--
+xp_gamedate :: PU UTCTime
+xp_gamedate =
+  (to_gamedate, from_gamedate) `xpWrapMaybe` xpText
   where
-    to_team_id :: String -> Maybe Int
-    to_team_id = readMaybe
+    format = "%A, %B %-d"
+
+    to_gamedate :: String -> Maybe UTCTime
+    to_gamedate s =
+      parseTime defaultTimeLocale format s'
+      where
+        s' = case (reverse s) of
+               (c2:c1:cs) -> let suffix = [c1,c2]
+                             in
+                               case suffix of
+                                 "st" -> reverse cs
+                                 "nd" -> reverse cs
+                                 "rd" -> reverse cs
+                                 "th" -> reverse cs
+                                 _    -> s -- Unknown suffix, leave it alone.
+               _ -> s -- The String is less than two characters long,
+                      -- leave it alone.
+
+
+    from_gamedate :: UTCTime -> String
+    from_gamedate d = s ++ (suffix s)
+      where
+        s = formatTime defaultTimeLocale format d
+
+        suffix :: String -> String
+        suffix cs =
+          case (reverse cs) of
+            []       -> []
+            ('1':_) -> "st"
+            ('2':_) -> "nd"
+            ('3':_) -> "rd"
+            _        -> "th"
+
+
+
+-- | The time format string used in 'xp_time' and 'xp_time_stamp'.
+--
+xp_time_format :: String
+xp_time_format = "%I:%M %p"
+
+
+-- | (Un)pickle a UTCTime without the date portion.
+--
+xp_time :: PU UTCTime
+xp_time =
+  (to_time, from_time) `xpWrapMaybe` xpText
+  where
+    to_time :: String -> Maybe UTCTime
+    to_time = parseTime defaultTimeLocale xp_time_format
+
+    from_time :: UTCTime -> String
+    from_time = formatTime defaultTimeLocale xp_time_format
+
+
+-- | (Un)pickle a UTCTime without the date portion, allowing for a
+--   value of \"TBA\" (which gets translated to 'Nothing').
+--
+xp_tba_time :: PU (Maybe UTCTime)
+xp_tba_time =
+  (to_time, from_time) `xpWrap` xpText
+  where
+    to_time :: String -> Maybe UTCTime
+    to_time s
+      | s == "TBA" = Nothing
+      | otherwise = parseTime defaultTimeLocale xp_time_format s
+
+    from_time :: Maybe UTCTime -> String
+    from_time Nothing = ""
+    from_time (Just t) = formatTime defaultTimeLocale xp_time_format t
+
+
+-- | (Un)pickle the \<time_stamp\> element format to/from a 'UTCTime'.
+--
+--   Example:  \<time_stamp\> January 6, 2014, at 10:11 PM ET \</time_stamp\>
+--
+--   TSN doesn't provide a proper time zone name, so we assume that
+--   it's always Eastern Standard Time. EST is UTC-5, so we
+--   add/subtract 5 hours to convert to/from UTC.
+--
+xp_time_stamp :: PU UTCTime
+xp_time_stamp =
+  (to_time_stamp, from_time_stamp) `xpWrapMaybe` xpText
+  where
+    -- This omits the timezone and trailing space.
+    format = " %B %-d, %Y, at " ++ xp_time_format ++ " ET "
+
+    five_hours :: NominalDiffTime
+    five_hours = 5 * 60 * 60
+
+    add_five :: UTCTime -> UTCTime
+    add_five = addUTCTime five_hours
+
+    subtract_five :: UTCTime -> UTCTime
+    subtract_five = addUTCTime (-1 * five_hours)
+
+    to_time_stamp :: String -> Maybe UTCTime
+    to_time_stamp = fmap add_five . parseTime defaultTimeLocale format
 
-    from_team_id :: Int -> String
-    from_team_id = printf "%03d"
+    from_time_stamp :: UTCTime -> String
+    from_time_stamp = formatTime defaultTimeLocale format . subtract_five