hodatime-0.1.1.1: src/Data/HodaTime/Instant.hs
-----------------------------------------------------------------------------
-- |
-- Module : Data.HodaTime.Instant
-- Copyright : (C) 2016 Jason Johnson
-- License : BSD-style (see the file LICENSE)
-- Maintainer : Jason Johnson <jason.johnson.081@gmail.com>
-- 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