packages feed

atelier-core-0.6.0.0: src/Atelier/Time.hs

-- | Strongly-typed time units and conversions.
--
-- @
-- nominalDiffTime 1.5 :: Millisecond        -- 1.5s rounded to 1500ms
-- convertUnit (5 :: Minute) :: Second       -- 300
-- toMicroseconds (2 :: Second)              -- 2000000
-- @
module Atelier.Time
    ( -- * Time units
      TimeUnit
    , Microsecond
    , Millisecond
    , Second
    , Minute
    , Hour

      -- * Conversions
    , nominalDiffTime
    , fromMicroseconds
    , toMicroseconds
    , convertUnit

      -- * Utility newtypes for converting time to and from JSON
    , AsJsonMicrosecond (..)
    , AsRawUnit (..)
    )
where

import Data.Aeson (FromJSON (..), ToJSON (..))
import Data.Time (NominalDiffTime)
import Data.Time.Units
    ( Hour
    , Microsecond
    , Millisecond
    , Minute
    , Second
    , TimeUnit (..)
    , convertUnit
    )


-- | Convert a 'NominalDiffTime' to any 'TimeUnit', rounding to the nearest
-- whole unit (the conversion goes through microsecond precision).
nominalDiffTime :: (TimeUnit t) => NominalDiffTime -> t
nominalDiffTime = fromMicroseconds . round @Double . (* 1_000_000) . realToFrac


newtype AsJsonMicrosecond unit = AsJsonMicrosecond {getAsJsonMicroseconds :: unit}
    deriving stock (Eq, Generic, Show)


instance (TimeUnit unit) => ToJSON (AsJsonMicrosecond unit) where
    toJSON = toJSON . toMicroseconds . getAsJsonMicroseconds


instance (TimeUnit unit) => FromJSON (AsJsonMicrosecond unit) where
    parseJSON = fmap (AsJsonMicrosecond . fromMicroseconds) . parseJSON


newtype AsRawUnit unit = AsRawUnit {getAsRawUnit :: unit}
    deriving stock (Eq, Generic, Show)


instance (Integral unit) => ToJSON (AsRawUnit unit) where
    toJSON = toJSON . toInteger . getAsRawUnit


instance (Integral unit) => FromJSON (AsRawUnit unit) where
    parseJSON = fmap (AsRawUnit . fromInteger) . parseJSON