{-# LANGUAGE CPP, OverloadedStrings #-}
module Network.Wai.Logger.Date (
DateCacheGetter
, DateCacheUpdater
, ZonedDate
, DateCacheConf(..)
, zonedDateCacheConf
, clockDateCacher
) where
import Control.Applicative ((<$>))
import Data.ByteString (ByteString)
import Data.IORef (newIORef, readIORef, writeIORef)
#if WINDOWS
import qualified Data.ByteString.Char8 as BS
import Data.Time
import System.Locale
#else
import Data.UnixTime (formatUnixTime, fromEpochTime)
import System.Posix (EpochTime, epochTime)
#endif
type DateCacheGetter = IO ZonedDate
type DateCacheUpdater = IO ()
type ZonedDate = ByteString
data DateCacheConf t = DateCacheConf {
getTime :: IO t
, formatDate :: t -> IO ByteString
}
#if WINDOWS
zonedDateCacheConf :: DateCacheConf UTCTime
zonedDateCacheConf = DateCacheConf {
getTime = getCurrentTime
, formatDate = \ut -> do
zt <- utcToLocalZonedTime ut
return $ BS.pack $ formatTime defaultTimeLocale "%d/%b/%Y:%T %z" zt
}
#else
zonedDateCacheConf :: DateCacheConf EpochTime
zonedDateCacheConf = DateCacheConf {
getTime = epochTime
, formatDate = formatUnixTime "%d/%b/%Y:%T %z" . fromEpochTime
}
#endif
data DateCache t = DateCache {
timeKey :: !t
, formattedDate :: !ByteString
} deriving (Eq, Show)
newDate :: DateCacheConf t -> t -> IO (DateCache t)
newDate setting tm = DateCache tm <$> formatDate setting tm
clockDateCacher :: IO (DateCacheGetter, DateCacheUpdater)
clockDateCacher = do
ref <- getTime zonedDateCacheConf >>= newDate zonedDateCacheConf >>= newIORef
return (getter ref, clock ref)
where
getter ref = formattedDate <$> readIORef ref
clock ref = do
tm <- getTime zonedDateCacheConf
date <- formatDate zonedDateCacheConf tm
let new = DateCache {
timeKey = tm
, formattedDate = date
}
writeIORef ref new