module Snap.Internal.Http.Server.Date
( getDateString
, getLogDateString
, getCurrentDateTime) where
import Control.Concurrent
import Control.Exception
import Control.Monad
import Data.ByteString (ByteString)
import Data.ByteString.Internal (c2w)
import qualified Data.ByteString as B
import Data.IORef
import Data.Time.Clock
import Data.Time.LocalTime
import Data.Time.Format
import System.IO.Unsafe
import System.Locale
data DateState = DateState {
_cachedDateString :: !(IORef ByteString)
, _cachedLogString :: !(IORef ByteString)
, _cachedDate :: !(IORef UTCTime)
, _valueIsOld :: !(IORef Bool)
, _morePlease :: !(MVar ())
, _dataAvailable :: !(MVar ())
, _dateThread :: !(MVar ThreadId)
}
dateState :: DateState
dateState = unsafePerformIO $ do
(s1,s2,date) <- fetchTime
bs1 <- newIORef s1
bs2 <- newIORef s2
dt <- newIORef date
ov <- newIORef False
th <- newEmptyMVar
mp <- newMVar ()
da <- newMVar ()
let d = DateState bs1 bs2 dt ov mp da th
t <- forkIO $ dateThread d
putMVar th t
return d
fetchTime :: IO (ByteString,ByteString,UTCTime)
fetchTime = do
now <- getCurrentTime
zt <- liftM zonedTimeToLocalTime getZonedTime
return (t1 now, t2 zt, now)
where
t1 now = B.pack $ map c2w $
formatTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S GMT" now
t2 now = B.pack $ map c2w $
formatTime defaultTimeLocale "%d/%b/%Y:%H:%M:%S %z" now
dateThread :: DateState -> IO ()
dateThread ds@(DateState dateString logString time valueIsOld morePlease
dataAvailable _) = do
takeMVar morePlease
(s1,s2,now) <- fetchTime
atomicModifyIORef dateString $ const (s1,())
atomicModifyIORef logString $ const (s2,())
atomicModifyIORef time $ const (now,())
writeIORef valueIsOld False
tryPutMVar dataAvailable ()
threadDelay 2000000
takeMVar dataAvailable
writeIORef valueIsOld True
dateThread ds
ensureFreshDate :: IO ()
ensureFreshDate = block $ do
old <- readIORef $ _valueIsOld dateState
when old $ do
tryPutMVar (_morePlease dateState) ()
readMVar $ _dataAvailable dateState
getDateString :: IO ByteString
getDateString = block $ do
ensureFreshDate
readIORef $ _cachedDateString dateState
getLogDateString :: IO ByteString
getLogDateString = block $ do
ensureFreshDate
readIORef $ _cachedLogString dateState
getCurrentDateTime :: IO UTCTime
getCurrentDateTime = block $ do
ensureFreshDate
readIORef $ _cachedDate dateState