----------------------------------------------------------------------------- -- | -- Module : Data.HodaTime.Instant -- Copyright : (C) 2016 Jason Johnson -- License : BSD-style (see the file LICENSE) -- Maintainer : Jason Johnson -- Stability : experimental -- Portability : TBD -- -- An 'Instant' is universal fixed moment in time. ---------------------------------------------------------------------------- module Data.HodaTime.Instant ( -- * Types Instant -- * Constructors ,fromSecondsSinceUnixEpoch ,now -- * Math ,add ,difference ,minus -- * Conversion ,inUtc -- * Debug - to be removed ,LTI.fromInstant -- TODO: REMOVE THIS! This is only exported for testing, remove it immediately after fixing fromSecondsSinceUnixEpoch ) where import Data.HodaTime.Instant.Internal import Data.HodaTime.Instant.Clock (now) import Data.HodaTime.Constants (secondsPerDay, nsecsPerSecond) import Data.HodaTime.Duration.Internal (Duration(..)) import Data.HodaTime.OffsetDateTime.Internal(Offset(..)) import Data.HodaTime.TimeZone.Internal (TimeZone(..)) import Data.HodaTime.ZonedDateTime.Internal (ZonedDateTime(..)) import qualified Data.HodaTime.OffsetDateTime.Internal as Offset (empty) import qualified Data.HodaTime.Duration.Internal as D import qualified Data.HodaTime.LocalTime.Internal as LTI (fromInstant) -- | Create an 'Instant' from an 'Int' that represents a Unix Epoch fromSecondsSinceUnixEpoch :: Int -> Instant fromSecondsSinceUnixEpoch s = fromUnixGetTimeOfDay s 0 -- | Add a 'Duration' to an 'Instant' to get a future 'Instant'. /NOTE: does not handle all negative durations, use 'minus'/ add :: Instant -> Duration -> Instant add (Instant ldays lsecs lnsecs) (Duration (Instant rdays rsecs rnsecs)) = Instant days' secs'' nsecs' where days = ldays + rdays secs = lsecs + rsecs nsecs = lnsecs + rnsecs (secs', nsecs') = adjust secs nsecs nsecsPerSecond (days', secs'') = adjust days secs' secondsPerDay adjust big small size | small >= size = (succ big, small - size) | otherwise = (big, small) -- | Get the difference between two instances difference :: Instant -> Instant -> Duration difference (Instant ldays lsecs lnsecs) (Instant rdays rsecs rnsecs) = Duration $ Instant days' (fromIntegral secs'') (fromIntegral nsecs') where days = ldays - rdays secs = (fromIntegral lsecs - fromIntegral rsecs) :: Int -- TODO: We should specify exactly what sizes we need here. Keep in mind we can depend that secs and nsecs are never negative so nsecs = (fromIntegral lnsecs - fromIntegral rnsecs) :: Int -- TODO: there is no worry that we get e.g. (-nsecsPerSecond - -nsecsPerSecond) causing us to have more than nsecsPerSecond. (secs', nsecs') = normalize nsecs secs nsecsPerSecond (days', secs'') = normalize secs' days secondsPerDay normalize x bigger size | x < 0 = (pred bigger, x + size) | otherwise = (bigger, x) -- | Subtract a 'Duration' from an 'Instant' to get an 'Instant' in the past. /NOTE: does not handle negative durations, use 'add'/ minus :: Instant -> Duration -> Instant minus linstant (Duration rinstant) = getInstant $ difference linstant rinstant -- Conversion {-^} -- | Create an 'OffsetDateTime' from this Instant and an Offset withOffset :: Instant -> Offset -> Calendar -> OffsetDateTime withOffset instant offset calendar = OffsetDateTime (LocalDateTime date time) offset -- TODO: I'm not sure I like applying the offset on construction. See if we can defer it where instant' = instant `add` (D.fromSeconds . fromIntegral . offsetSeconds $ offset) time = LTI.fromInstant instant' date | calendar == Gregorian || calendar == Iso = GI.fromInstantInCalendar instant' calendar | otherwise = undefined -- TODO: Why does compiler think this isn't total without the otherwise? -- | Convert 'Instant' Into a 'ZonedDateTime' based on the supplied 'TimeZone' and 'Calendar' inZone :: Instant -> TimeZone -> Calendar -> ZonedDateTime inZone instant UTCzone calendar = ZonedDateTime odt UTCzone where odt = withOffset instant Offset.empty calendar inZone instant tzi@TimeZone { } calendar = ZonedDateTime odt tzi where odt = withOffset instant offset calendar offset | otherwise = undefined -- TODO: When TimeZone module is implemented we can finish this (look at the olson time zone series from hackage, but we can't use it all) -} -- | Convert 'Instant' to a 'ZonedDateTime' in the UTC time zone, ISO calendar inUtc :: Instant -> ZonedDateTime inUtc instant = undefined