Frames-0.7.4: demo/TimeZones/src/TimeIn.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE RankNTypes #-}
-- | Define the 'TimeIn' type that lets us specify in the type how a
-- 'LocalTime' should be converted to a 'UTCTime'.
module TimeIn where
import Control.Monad (MonadPlus (mzero), msum)
import qualified Data.Text as T
import Data.Time.Clock
import Data.Time.Format
import Data.Time.LocalTime
import Data.Time.Zones
import Data.Time.Zones.TH
import Frames.ColumnTypeable (Parseable(..), Parsed(..))
import GHC.TypeLits
import Language.Haskell.TH
-- | A 'UTCTime' tagged with a symbol denoting the 'TZ' time zone from
-- whence it came.
newtype TimeIn (zone :: Symbol) = TimeIn UTCTime deriving Show
failZero :: MonadPlus m => Maybe r -> m r
failZero = maybe mzero pure
-- | Try to parse a 'LocalTime' value using common formats.
parseLocalTime :: MonadPlus m => T.Text -> m LocalTime
parseLocalTime t = msum (map (($ T.unpack t) . mkParser) formats)
where formats = ["%F %T", "%F"]
mkParser = (failZero .) . parseTimeM True defaultTimeLocale
-- | @zonedTime "America/Chicago"@ will create a 'Parseable' instance
-- for the type @TimeIn "America/Chicago"@. You can then use this type
-- when loading data.
timeIn :: String -> DecsQ
timeIn tzStr =
do let fromLocal = [e| localTimeToUTCTZ $(includeTZFromDB tzStr) |]
ex = [e| fmap (Definitely . TimeIn . $fromLocal) . parseLocalTime |]
sequenceA [
instanceD (pure [])
[t|Parseable (TimeIn $(pure $ LitT (StrTyLit tzStr)))|]
[ funD (mkName "parse") [clause [] (normalB ex) []] ] ]