htsn-import-0.0.4: src/TSN/Picklers.hs
-- | (Un)picklers for data types present in The Sports Network XML
-- feed.
--
module TSN.Picklers (
xp_date,
xp_gamedate,
xp_tba_time,
xp_time,
xp_time_stamp )
where
-- System imports.
import Data.Time.Clock ( NominalDiffTime, UTCTime, addUTCTime )
import Data.Time.Format ( formatTime, parseTime )
import System.Locale ( defaultTimeLocale )
import Text.XML.HXT.Arrow.Pickle (
xpText,
xpWrap,
xpWrapMaybe )
import Text.XML.HXT.Arrow.Pickle.Xml ( PU )
-- | (Un)pickle a UTCTime without the time portion.
--
xp_date :: PU UTCTime
xp_date =
(to_date, from_date) `xpWrapMaybe` xpText
where
format = "%-m/%-d/%Y"
to_date :: String -> Maybe UTCTime
to_date = parseTime defaultTimeLocale format
from_date :: UTCTime -> String
from_date = formatTime defaultTimeLocale format
-- | (Un)pickle a UTCTime from a weather forecast's gamedate. Example
-- input looks like,
--
-- \<forecast gamedate=\"Monday, December 30th\"\>
--
-- 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
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_time_stamp :: UTCTime -> String
from_time_stamp = formatTime defaultTimeLocale format . subtract_five