-- | -- Formatting time is slow. -- This package provides mechanisms to cache formatted date. module System.Date.Cache ( -- * Types DateCacheConf(..) , DateCacheGetter , DateCacheCloser -- * Date cacher , ondemandDateCacher , clockDateCacher ) where import Control.Applicative import Control.Concurrent import Data.ByteString (ByteString) import Data.IORef type DateCacheGetter = IO ByteString type DateCacheCloser = IO () data DateCache t = DateCache { timeKey :: !t , formattedDate :: !ByteString } deriving (Eq, Show) data DateCacheConf t = DateCacheConf { -- | A function to get a time. E.g 'epochTime' and 'getCurrentTime'. getTime :: IO t -- | A function to format a time. , formatDate :: t -> IO ByteString } newDate :: DateCacheConf t -> t -> IO (DateCache t) newDate setting tm = DateCache tm <$> formatDate setting tm -- | -- Date cacher which gets a time and formatted it only when -- returned getter is executed. ondemandDateCacher :: Eq t => DateCacheConf t -> IO (DateCacheGetter, DateCacheCloser) ondemandDateCacher setting = do ref <- getTime setting >>= newDate setting >>= newIORef return $! (getter ref, closer) where getter ref = do newTm <- getTime setting cache <- readIORef ref let oldTm = timeKey cache if oldTm == newTm then return $ formattedDate cache else do newCache <- newDate setting newTm writeIORef ref newCache return $ formattedDate newCache closer = return () -- | -- Date cacher which gets a time and formatted it every second. -- This returns a getter. clockDateCacher :: Eq t => DateCacheConf t -> IO (DateCacheGetter, DateCacheCloser) clockDateCacher setting = do ref <- getTime setting >>= newDate setting >>= newIORef tid <- forkIO $ clock ref return $! (getter ref, closer tid) where getter ref = formattedDate <$> readIORef ref clock ref = do threadDelay 1000000 tm <- getTime setting date <- formatDate setting tm let new = DateCache { timeKey = tm , formattedDate = date } writeIORef ref new clock ref closer tid = killThread tid