{-# OPTIONS_GHC -Wno-unused-imports #-}
{-# LANGUAGE ExistentialQuantification #-}
{- |
Module : Time.Time
License : BSD-style
Copyright : (c) 2014 Vincent Hanquez <vincent@snarc.org>
Stability : experimental
Portability : unknown
Types representing points in time, and associated type classes, and durations
of time, and associated functions.
-}
module Time.Time
( -- * Classes for conversion
Time (..)
, Timeable (..)
-- * Conversion
, timeConvert
-- * Date and time
, timeGetDate
, timeGetDateTimeOfDay
, timeGetTimeOfDay
-- * Arithmetic
, Duration (..)
, Period (..)
, timeAdd
, timeDiff
, timeDiffP
, dateAddPeriod
) where
import Foreign.C.Types ( CTime (..) )
import Time.Calendar ( dateTimeToUnixEpoch )
import Time.Diff
( Duration (..), Period (..), dateAddPeriod
, elapsedTimeAddSecondsP
)
import Time.Internal ( dateTimeFromUnixEpoch, dateTimeFromUnixEpochP )
import Time.Types
( Date (..), DateTime (..), Elapsed (..), ElapsedP (..)
, Month (..), NanoSeconds (..), Seconds (..)
, TimeInterval (..), TimeOfDay (..)
)
-- | A type class promising functionality for:
--
-- * converting a value of the type in question to a t'Elapsed' value or
-- a t'ElapsedP' value; and
--
-- * yielding separately a nanoseconds component of the value of the type in
-- question (should yield @0@ when the type is less precise than seconds).
--
class Timeable t where
-- | Convert the given value to the number of non-leap seconds and
-- nanoseconds elapsed since the Unix epoch (1970-01-01 00:00:00 UTC).
timeGetElapsedP :: t -> ElapsedP
-- | Convert the given value to the number of non-leap seconds elapsed since
-- the Unix epoch (1970-01-01 00:00:00 UTC).
--
-- Defaults to 'timeGetElapsedP'.
timeGetElapsed :: t -> Elapsed
timeGetElapsed t = e
where
ElapsedP e _ = timeGetElapsedP t
-- | Optionally, for the given value, yield the number of nanoseconds
-- component.
--
-- If the type in question does not provide sub-second precision, should yield
-- @0@.
--
-- Defaults to 'timeGetElapsedP'. For efficiency, if the type in question does
-- not provide sub-second precision, it is a good idea to override this
-- method.
timeGetNanoSeconds :: t -> NanoSeconds
timeGetNanoSeconds t = ns where ElapsedP _ ns = timeGetElapsedP t
-- | A type class promising functionality for converting t'ElapsedP' values
-- and t'Elapsed' values to values of the type in question.
class Timeable t => Time t where
-- | Convert from a number of non-leap seconds and nanoseconds elapsed since
-- the Unix epoch (1970-01-01 00:00:00 UTC).
timeFromElapsedP :: ElapsedP -> t
-- | Convert from a number of non-leap seconds elapsed since the Unix epoch
-- (1970-01-01 00:00:00 UTC).
--
-- Defaults to 'timeFromElapsedP'.
timeFromElapsed :: Elapsed -> t
timeFromElapsed e = timeFromElapsedP (ElapsedP e 0)
instance Timeable CTime where
timeGetElapsedP c = ElapsedP (timeGetElapsed c) 0
timeGetElapsed (CTime c) = Elapsed (Seconds $ fromIntegral c)
timeGetNanoSeconds _ = 0
instance Time CTime where
timeFromElapsedP (ElapsedP e _) = timeFromElapsed e
timeFromElapsed (Elapsed (Seconds c)) = CTime (fromIntegral c)
instance Timeable Elapsed where
timeGetElapsedP e = ElapsedP e 0
timeGetElapsed e = e
timeGetNanoSeconds _ = 0
instance Time Elapsed where
timeFromElapsedP (ElapsedP e _) = e
timeFromElapsed e = e
instance Timeable ElapsedP where
timeGetElapsedP e = e
timeGetNanoSeconds (ElapsedP _ ns) = ns
instance Time ElapsedP where
timeFromElapsedP e = e
instance Timeable Date where
timeGetElapsedP d = timeGetElapsedP (DateTime d (TimeOfDay 0 0 0 0))
instance Time Date where
timeFromElapsedP (ElapsedP elapsed _) = d
where
(DateTime d _) = dateTimeFromUnixEpoch elapsed
instance Timeable DateTime where
timeGetElapsedP d = ElapsedP (dateTimeToUnixEpoch d) (timeGetNanoSeconds d)
timeGetElapsed = dateTimeToUnixEpoch
timeGetNanoSeconds (DateTime _ (TimeOfDay _ _ _ ns)) = ns
instance Time DateTime where
timeFromElapsedP = dateTimeFromUnixEpochP
-- | Convert from one time representation to another. This will not compile
-- unless the compiler can infer the types.
--
-- Specialized functions are available for built-in types:
--
-- * 'timeGetDate'
--
-- * 'timeGetDateTimeOfDay'
--
-- * 'timeGetElapsed'
--
-- * 'timeGetElapsedP'
timeConvert :: (Timeable t1, Time t2) => t1 -> t2
timeConvert t1 = timeFromElapsedP (timeGetElapsedP t1)
{-# INLINE[2] timeConvert #-}
{-# RULES "timeConvert/ID" timeConvert = id #-}
{-# RULES "timeConvert/ElapsedP" timeConvert = timeGetElapsedP #-}
{-# RULES "timeConvert/Elapsed" timeConvert = timeGetElapsed #-}
-- | For the given value of a point in time, yield the corresponding date (a
-- specialization of 'timeConvert').
timeGetDate :: Timeable t => t -> Date
timeGetDate t = d where (DateTime d _) = timeGetDateTimeOfDay t
{-# INLINE[2] timeGetDate #-}
{-# RULES "timeGetDate/ID" timeGetDate = id #-}
{-# RULES "timeGetDate/DateTime" timeGetDate = dtDate #-}
-- | For the given value for a point in time, yield the corresponding time
-- (a specialization of 'timeConvert').
timeGetTimeOfDay :: Timeable t => t -> TimeOfDay
timeGetTimeOfDay t = tod where (DateTime _ tod) = timeGetDateTimeOfDay t
{-# INLINE[2] timeGetTimeOfDay #-}
{-# RULES "timeGetTimeOfDay/Date" timeGetTimeOfDay = const (TimeOfDay 0 0 0 0) #-}
{-# RULES "timeGetTimeOfDay/DateTime" timeGetTimeOfDay = dtTime #-}
-- | For the given value for a point in time, yield the corresponding date and
-- time (a specialization of 'timeConvert').
timeGetDateTimeOfDay :: Timeable t => t -> DateTime
timeGetDateTimeOfDay t = dateTimeFromUnixEpochP $ timeGetElapsedP t
{-# INLINE[2] timeGetDateTimeOfDay #-}
{-# RULES "timeGetDateTimeOfDay/ID" timeGetDateTimeOfDay = id #-}
{-# RULES "timeGetDateTimeOfDay/Date" timeGetDateTimeOfDay = flip DateTime (TimeOfDay 0 0 0 0) #-}
-- | Add the given period of time to the given value for a point in time,
-- assuming that the period of time is equated with a number of non-leap
-- seconds.
--
-- Example:
--
-- > t1 `timeAdd` mempty { durationHours = 12 }
--
-- Example:
--
-- >>> startDate = Date 2016 December 31 -- Date of last leap second
-- >>> preLeapSecond = TimeOfDay 23 59 59 0
-- >>> startDateTime = DateTime startDate preLeapSecond
-- >>> oneNonleapSecond = Duration 0 0 1 0 -- Assume non-leap seconds
-- >>> nextDate = Date 2017 January 1
-- >>> firstSecond = TimeOfDay 0 0 0 0
-- >>> endDateTime = DateTime nextDate firstSecond
-- >>> timeAdd startDateTime oneNonleapSecond == endDateTime
-- True
timeAdd :: (Time t, TimeInterval ti) => t -> ti -> t
timeAdd t ti =
timeFromElapsedP $ elapsedTimeAddSecondsP (timeGetElapsedP t) (toSeconds ti)
-- | For the two given points in time, yields the difference in non-leap seconds
-- between them.
--
-- Effectively:
--
-- > t2 `timeDiff` t1 = t2 - t1
timeDiff :: (Timeable t1, Timeable t2) => t1 -> t2 -> Seconds
timeDiff t1 t2 = sec where (Elapsed sec) = timeGetElapsed t1 - timeGetElapsed t2
-- | For the two given points in time, yields the difference in non-leap seconds
-- and nanoseconds between them.
--
-- Effectively:
--
-- > @t2 `timeDiffP` t1 = t2 - t1
timeDiffP :: (Timeable t1, Timeable t2) => t1 -> t2 -> (Seconds, NanoSeconds)
timeDiffP t1 t2 = (sec, ns)
where
(ElapsedP (Elapsed sec) ns) = timeGetElapsedP t1 - timeGetElapsedP t2