{-# LANGUAGE Safe #-} module Data.UTC.Class.IsTime ( IsTime (..) ) where import Control.Monad.Catch import Data.Ratio import Data.UTC.Internal -- | This class captures the concept of a 24-hour clock time -- during a day. class IsTime t where -- | Returns values in the range 0 to 23. hour :: t -> Integer -- | Returns values in the range 0 to 59. minute :: t -> Integer -- | Returns values in the range 0 to 59. second :: t -> Integer -- | Returns values in the range 0.0 <= x < 1.0. secondFraction :: t -> Rational -- | Accepts values in the range 0 to 23. -- -- The function fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime'). setHour :: (MonadThrow m) => Integer -> t -> m t -- | Accepts values in the range 0 to 59. -- -- The function fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime'). setMinute :: (MonadThrow m) => Integer -> t -> m t -- | Accepts values in the range 0 to 59. -- -- The function fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime'). setSecond :: (MonadThrow m) => Integer -> t -> m t -- | Accepts values in the range 0.0 <= x < 1.0. -- -- The function fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime'). setSecondFraction :: (MonadThrow m) => Rational -> t -> m t -- | Adds an arbitrary count of hours (positive or negative). -- -- * Full days flow over to 'Data.UTC.addDays' if the type is also an instance of 'Data.UTC.Class.IsDate' (this is the case for 'Data.UTC.DateTime'). -- * Types not implementing the 'Data.UTC.Class.IsDate' class should just ignore the days part on overflow (like 'Data.UTC.Time' does). -- * Fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime'). addHours :: (MonadThrow m) => Integer -> t -> m t addHours h t = setHour hors t where h' = h + (hour t) hors = h' `mod` hoursPerDay -- | Adds an arbitrary count of minutes (positive or negative). -- -- * Full hours flow over to 'addHours'. -- * Fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime'). addMinutes :: (MonadThrow m) => Integer -> t -> m t addMinutes m t = setMinute mins t >>= addHours hors where m' = m + (minute t) mins = m' `mod` minsPerHour hors = m' `div` minsPerHour -- | Adds an arbitrary count of seconds (positive or negative). -- -- * Full minutes flow over to 'addMinutes'. -- * Fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime'). addSeconds :: (MonadThrow m) => Integer -> t -> m t addSeconds s t = setSecond secs t >>= addMinutes mins where s' = s + (second t) secs = s' `mod` secsPerMinute mins = s' `div` secsPerMinute -- | Adds an arbitrary second fraction (positive or negative). -- -- * Full seconds flow over to 'addSeconds'. -- * Instances of this class are not required to preserve full precision (although 'Data.UTC.Time' and 'Data.UTC.DateTime' do so). -- * Fails if the result cannot be represented by the type (cannot happen for 'Data.UTC.Time' and 'Data.UTC.DateTime'). addSecondFractions :: (MonadThrow m) => Rational -> t -> m t addSecondFractions f t | f == 0 = return t | f >= 0 = setSecondFraction frcs t >>= addSeconds secs | otherwise = setSecondFraction (frcs + 1.0) t >>= addSeconds (secs - 1) where f' = f + (secondFraction t) frcs = f' - (truncate f' % 1) secs = truncate f'