module Yore.Schedule (schedule) where import Control.Concurrent (forkIO, threadDelay) import Control.Monad (when) import Data.Functor (($>)) import Data.Time (ZonedTime, getZonedTime, nominalDiffTimeToSeconds, secondsToNominalDiffTime) import Data.Time.Clock.POSIX (getPOSIXTime) schedule :: (ZonedTime -> Bool) -> IO a -> IO () schedule shouldRunAt f = everyMinute $ \now -> when (shouldRunAt now) (f $> ()) everyMinute :: (ZonedTime -> IO ()) -> IO () everyMinute f = do -- Use POSIX time to avoid having to handle leap seconds now <- getPOSIXTime let posixSeconds :: Int posixSeconds = floor $ nominalDiffTimeToSeconds now lastFullMinute = secondsToNominalDiffTime $ fromIntegral $ posixSeconds - posixSeconds `mod` 60 secondsSinceLastFullMinute = now - lastFullMinute threadDelay $ ceiling $ (60 - secondsSinceLastFullMinute) * 1_000_000 _ <- forkIO $ f =<< getZonedTime everyMinute f