non-negative-time-diff: type safe diffUTCTime
Both arguments of diffUTCTime function from time package have the
same type. It is easy to mix them.
f = do started <- getCurrentTime threadDelay 10_000_000 ended <- getCurrentTime pure $ started `diffUTCTime` ended
This package provides a stricter diffUTCTime that significantly
reduces possibility of mixing its arguments by an accident.
import Data.Time.Clock.NonNegativeTimeDiff f = do started <- getCurrentTime threadDelay 10_000_000 ended <- getTimeAfter started pure $ ended `diffUTCTime` started
STM use case
The STM package is shipped without a function to get current time. Let’s consider a situtation like this:
data Ctx
= Ctx { m :: Map Int UTCTime
, s :: TVar NominalDiffTime
, q :: TQueue Int
}
f (c :: Ctx) = do
now <- getCurrentTime
atomically $ do
i <- readTQueue q
lookup i c.m >>= \case
Nothing -> pure ()
Just t -> modifyTVar' c.s (+ diffUTCTime now t)now might be less than t because the queue might be empty by the
time f is invoked. The package API can correct the above snippet as
follows:
data Ctx
= Ctx { m :: Map Int UtcBox
, s :: TVar NominalDiffTime
, q :: TQueue Int
}
f (c :: Ctx) = do
atomically $ do
i <- readTQueue q
lookup i c.m >>= \case
Nothing -> pure ()
Just t ->
doAfter tb \t -> do
now <- getTimeAfter t
modifyTVar' c.s (+ diffUTCTime now t)File access time
Another popular usecase where original diffUTCTime might be misused.
isFileOlderThan :: FilePath -> NominalDiffTime -> IO Bool
isFileOlderThan fp maxAge = do
now <- getCurrentTime
mt <- getModificationTime fp
when (mt `diffUTCTime` now > maxAge) $ do
removeFile fpFile age is always negative in the above example - this eventually would cause a space leak on disk.
Corrected version:
isFileOlderThan :: FilePath -> NominalDiffTime -> IO Bool
isFileOlderThan fp maxAge =
getModificationTime fp >>= (`doAfter` \mt -> do
now <- getTimeAfter mt
when (now `diffUTCTime` mt > maxAge) $ do
removeFile fp)Requirements
Unboxing UtcBox values requires a GHC
natnormalise plugin:
{-# GHC_OPTIONS -fplugin GHC.TypeLits.Normalise #-}
Downloads
- non-negative-time-diff-0.0.1.tar.gz [browse] (Cabal source package)
- Package description (as included in the package)
Maintainer's Corner
For package maintainers and hackage trustees
Candidates
- No Candidates
| Versions [RSS] | 0.0.1 |
|---|---|
| Change log | changelog.md |
| Dependencies | aeson (<3), base (>=4.7 && <5), deepseq (<2), directory (<2), ghc-typelits-natnormalise (<1), mtl (<3), safecopy (<1), time (<2) [details] |
| Tested with | ghc ==9.12.2 |
| License | BSD-3-Clause |
| Copyright | Daniil Iaitkov 2026 |
| Author | Daniil Iaitskov |
| Maintainer | dyaitskov@gmail.com |
| Uploaded | by DaniilIaitskov at 2026-03-09T13:54:35Z |
| Category | System |
| Home page | http://github.com/yaitskov/non-negative-time-diff |
| Bug tracker | https://github.com/yaitskov/non-negative-time-diff/issues |
| Source repo | head: git clone https://github.com/yaitskov/non-negative-time-diff.git |
| Distributions | |
| Downloads | 1 total (1 in the last 30 days) |
| Rating | (no votes yet) [estimated by Bayesian average] |
| Your Rating | |
| Status | Docs uploaded by user Build status unknown [no reports yet] |