pretty-relative-time-0.0.0.0: src/Text/Time/Pretty/TimeAgo.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE RecordWildCards #-}
module Text.Time.Pretty.TimeAgo
( timeAgo
, timeAgoToDiffTime
, TimeAgo(..)
) where
import Data.Time
import Data.Validity
import GHC.Generics (Generic)
import Text.Time.Pretty.Constants
data TimeAgo = TimeAgo
{ signAgo :: Ordering
, daysAgo :: Integer
, hoursAgo :: Integer
, minutesAgo :: Integer
, secondsAgo :: Integer
, picoSecondsAgo :: Integer
} deriving (Show, Eq, Generic)
instance Validity TimeAgo where
isValid = isValidByValidating
validate TimeAgo {..} =
mconcat
[ (case signAgo of
EQ ->
and
[ daysAgo == 0
, hoursAgo == 0
, minutesAgo == 0
, secondsAgo == 0
, picoSecondsAgo == 0
]
_ ->
any
(> 0)
[ daysAgo
, hoursAgo
, minutesAgo
, secondsAgo
, picoSecondsAgo
]) <?@>
"the sign makes sense"
, (daysAgo >= 0) <?@> "days are positive"
, (hoursAgo < hoursPerDay) <?@> "hours < 24"
, (hoursAgo >= 0) <?@> "hours are positive"
, (minutesAgo < minutesPerHour) <?@> "minutes < 60"
, (minutesAgo >= 0) <?@> "minutes are positive"
, (secondsAgo < secondsPerMinute) <?@> "seconds < 60"
, (secondsAgo >= 0) <?@> "seconds are positive"
, (picoSecondsAgo < picoSecondsPerSecond) <?@> "picoseconds < 1E12"
, (picoSecondsAgo >= 0) <?@> "picoseconds are positive"
]
timeAgo :: NominalDiffTime -> TimeAgo
timeAgo dt = TimeAgo {..}
where
signAgo = compare dt 0
picoSecondsAgo =
totalPicoSecondsAgo - picoSecondsPerSecond * totalSecondsAgo
secondsAgo = totalSecondsAgo - secondsPerMinute * totalMinutesAgo
minutesAgo = totalMinutesAgo - minutesPerHour * totalHoursAgo
hoursAgo = totalHoursAgo - hoursPerDay * totalDaysAgo
daysAgo = totalDaysAgo
totalPicoSecondsAgo =
floor $ absDt * fromIntegral (picoSecondsPerSecond :: Integer)
totalSecondsAgo = floor absDt :: Integer
totalMinutesAgo = floor $ absDt / fromIntegral (secondsPerMinute :: Integer)
totalHoursAgo =
floor $
absDt / fromIntegral (minutesPerHour * secondsPerMinute :: Integer)
totalDaysAgo =
floor $
absDt /
fromIntegral
(hoursPerDay * minutesPerHour * secondsPerMinute :: Integer)
absDt = abs dt
timeAgoToDiffTime :: TimeAgo -> NominalDiffTime
timeAgoToDiffTime TimeAgo {..} =
(/ fromIntegral (picoSecondsPerSecond :: Integer)) $
realToFrac $
(case signAgo of
EQ -> const 0
GT -> id
LT -> negate)
(picoSecondsAgo +
picoSecondsPerSecond *
(secondsAgo + 60 * (minutesAgo + 60 * (hoursAgo + 24 * daysAgo))))