packages feed

hodatime-1.0.0.0: src/Data/HodaTime/OffsetDateTime.hs

-----------------------------------------------------------------------------
-- |
-- Module      :  Data.HodaTime.OffsetDateTime
-- Copyright   :  (C) 2016 Jason Johnson
-- License     :  BSD-style (see the file LICENSE)
-- Maintainer  :  Jason Johnson <jason.johnson.081@gmail.com>
-- Stability   :  experimental
-- Portability :  POSIX, Windows
--
-- An 'OffsetDateTime' is a date and time combined with an offset from UTC time.  'OffsetDateTime' is the form that HTTP uses to deal with dates and times.
----------------------------------------------------------------------------
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.HodaTime.OffsetDateTime
(
  -- * Types
   OffsetDateTime
  -- * Constructors
  ,fromInstantWithOffset
  ,fromCalendarDateTimeWithOffset
  -- * Math
  -- * Conversion
  ,toCalendarDateTime
  ,offset
)
where

import Data.HodaTime.Offset.Internal
import Data.HodaTime.Instant.Internal (Instant)
import Data.HodaTime.CalendarDateTime.Internal (CalendarDateTime, IsCalendarDateTime(..), Date)
import Data.HodaTime.ZonedDateTime.Internal (ZonedDateTime(..))
import Data.HodaTime.TimeZone.Internal (TimeZone(..), TZIdentifier(..), TransitionInfo, tiUtcOffset, fixedOffsetZone)
import Data.Hashable (Hashable(..))

-- | A 'CalendarDateTime' with a UTC offset.  This is the format used by e.g. HTTP.  This type has a fixed 'TimeZone' with the name "UTC(+/-)offset".  If the offset is
-- empty, the name of the 'TimeZone' will be UTC
newtype OffsetDateTime cal = OffsetDateTime (ZonedDateTime cal)

deriving instance Eq (Date cal) => Eq (OffsetDateTime cal)
deriving instance (IsCalendarDateTime cal, Eq (Date cal)) => Ord (OffsetDateTime cal)

-- | Renders an 'OffsetDateTime' as the 'fromInstantWithOffset' call that reconstructs it.
instance IsCalendarDateTime cal => Show (OffsetDateTime cal) where
  showsPrec p (OffsetDateTime (ZonedDateTime cdt _ ti)) = showParen (p > 10) $
      showString "fromInstantWithOffset " . showsPrec 11 inst . showChar ' ' . showsPrec 11 off
    where
      off = tiUtcOffset ti
      inst = adjustInstant (negateOffset off) (toUnadjustedInstant cdt)
      negateOffset (Offset s) = Offset (negate s)

-- NOTE: no 'NFData' instance is provided because 'OffsetDateTime' embeds a 'TimeZone', whose fingertree-based
-- transition maps cannot be forced (see 'Data.HodaTime.TimeZone.Internal').
instance Hashable (Date cal) => Hashable (OffsetDateTime cal) where
  hashWithSalt s (OffsetDateTime z) = hashWithSalt s z

-- | Create an 'OffsetDateTime' from an 'Instant' and an 'Offset'.
fromInstantWithOffset :: IsCalendarDateTime cal => Instant -> Offset -> OffsetDateTime cal
fromInstantWithOffset inst offset' = OffsetDateTime $ ZonedDateTime cdt tz tInfo
  where
    (tz, tInfo) = makeFixedTimeZone offset'
    cdt = fromAdjustedInstant . adjustInstant offset' $ inst

-- | Create an 'OffsetDateTime' from a 'CalendarDateTime' and an 'Offset'.
fromCalendarDateTimeWithOffset :: CalendarDateTime cal -> Offset -> OffsetDateTime cal
fromCalendarDateTimeWithOffset cdt offset' = OffsetDateTime $ ZonedDateTime cdt tz tInfo
  where
    (tz, tInfo) = makeFixedTimeZone offset'

-- | The local (wall-clock) 'CalendarDateTime' of the 'OffsetDateTime', before the offset is applied.
toCalendarDateTime :: OffsetDateTime cal -> CalendarDateTime cal
toCalendarDateTime (OffsetDateTime zdt) = zdtCalendarDateTime zdt

-- | The UTC 'Offset' of the 'OffsetDateTime'.
offset :: OffsetDateTime cal -> Offset
offset (OffsetDateTime zdt) = tiUtcOffset . zdtActiveTransition $ zdt

-- helper functions

makeFixedTimeZone :: Offset -> (TimeZone, TransitionInfo)
makeFixedTimeZone offset' = (TimeZone (Zone tzName) utcM calDateM, tInfo)
  where
    tzName = toStringRep offset'
    (utcM, calDateM, tInfo) = fixedOffsetZone tzName offset'