packages feed

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

{-# OPTIONS_GHC -Wno-orphans #-}

-- | Strongly-typed time units and conversions.
--
-- Re-exports the duration types from "Data.Time.Units" — so a single import
-- covers 'Microsecond', 'Millisecond', 'Second', 'Minute' and 'Hour' — and adds
-- conversion helpers plus JSON instances for those types. Each unit serialises
-- as an integer count of itself: a 'Second' as a whole number of seconds, a
-- 'Millisecond' as milliseconds, and so on.
--
-- Note: the 'FromJSON' and 'ToJSON' instances for the time-unit types are
-- orphans, defined here because neither @aeson@ nor @time-units@ provides them.
--
-- @
-- 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
    ) where

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


-- | 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


-- Orphan instances for JSON deserialization of time unit types.
-- All time units in Data.Time.Units wrap Integer, so we parse as Integer.

instance FromJSON Microsecond where
    parseJSON = fmap fromInteger . parseJSON


instance FromJSON Millisecond where
    parseJSON = fmap fromInteger . parseJSON


instance FromJSON Second where
    parseJSON = fmap fromInteger . parseJSON


instance FromJSON Minute where
    parseJSON = fmap fromInteger . parseJSON


instance FromJSON Hour where
    parseJSON = fmap fromInteger . parseJSON


instance ToJSON Microsecond where
    toJSON us = toJSON (toMicroseconds us)


instance ToJSON Millisecond where
    toJSON ms = toJSON (toMicroseconds ms `div` 1000)


instance ToJSON Second where
    toJSON s = toJSON (toMicroseconds s `div` 1_000_000)


instance ToJSON Minute where
    toJSON m = toJSON (toMicroseconds m `div` 60_000_000)


instance ToJSON Hour where
    toJSON h = toJSON (toMicroseconds h `div` 3_600_000_000)