module Data.HodaTime.TimeZone.Internal
(
   TZIdentifier(..)
  ,TransitionInfo(..)
  ,TransitionExpression(..)
  ,TransitionExpressionInfo(..)
  ,UtcTransitionsMap
  ,IntervalEntry(..)
  ,CalDateTransitionsMap
  ,TimeZone(..)
  ,emptyUtcTransitions
  ,addUtcTransition
  ,addUtcTransitionExpression
  ,activeTransitionFor
  ,emptyCalDateTransitions
  ,addCalDateTransition
  ,addCalDateTransitionExpression
  ,calDateTransitionsFor
  ,aroundCalDateTransition
  ,fixedOffsetZone
  ,expressionToInstant
  ,yearExpressionToInstant
)
where

import Data.Maybe (fromMaybe)
import Data.HodaTime.Instant.Internal (Instant(..), minus, bigBang)
import Data.HodaTime.Offset.Internal (Offset(..), adjustInstant)
import Data.HodaTime.Duration.Internal (fromNanoseconds)
import Data.HodaTime.Calendar.Gregorian.Internal (nthDayToDayOfMonth, yearMonthDayToDays, maxDaysInMonth, instantToYearMonthDay)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.IntervalMap.FingerTree (IntervalMap, Interval(..))
import qualified Data.IntervalMap.FingerTree as IMap
import Data.Hashable (Hashable(..))

data TZIdentifier = UTC | Zone String
  deriving (TZIdentifier -> TZIdentifier -> Bool
(TZIdentifier -> TZIdentifier -> Bool)
-> (TZIdentifier -> TZIdentifier -> Bool) -> Eq TZIdentifier
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TZIdentifier -> TZIdentifier -> Bool
== :: TZIdentifier -> TZIdentifier -> Bool
$c/= :: TZIdentifier -> TZIdentifier -> Bool
/= :: TZIdentifier -> TZIdentifier -> Bool
Eq, Int -> TZIdentifier -> ShowS
[TZIdentifier] -> ShowS
TZIdentifier -> String
(Int -> TZIdentifier -> ShowS)
-> (TZIdentifier -> String)
-> ([TZIdentifier] -> ShowS)
-> Show TZIdentifier
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TZIdentifier -> ShowS
showsPrec :: Int -> TZIdentifier -> ShowS
$cshow :: TZIdentifier -> String
show :: TZIdentifier -> String
$cshowList :: [TZIdentifier] -> ShowS
showList :: [TZIdentifier] -> ShowS
Show)

instance Hashable TZIdentifier where
  hashWithSalt :: Int -> TZIdentifier -> Int
hashWithSalt Int
s TZIdentifier
UTC      = Int -> Int -> Int
forall a. Hashable a => Int -> a -> Int
hashWithSalt Int
s (Int
0 :: Int)
  hashWithSalt Int
s (Zone String
n) = Int
s Int -> Int -> Int
forall a. Hashable a => Int -> a -> Int
`hashWithSalt` (Int
1 :: Int) Int -> String -> Int
forall a. Hashable a => Int -> a -> Int
`hashWithSalt` String
n

data TransitionInfo = TransitionInfo { TransitionInfo -> Offset
tiUtcOffset :: Offset, TransitionInfo -> Bool
tiIsDst :: Bool, TransitionInfo -> String
tiAbbreviation :: String }
  deriving (TransitionInfo -> TransitionInfo -> Bool
(TransitionInfo -> TransitionInfo -> Bool)
-> (TransitionInfo -> TransitionInfo -> Bool) -> Eq TransitionInfo
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TransitionInfo -> TransitionInfo -> Bool
== :: TransitionInfo -> TransitionInfo -> Bool
$c/= :: TransitionInfo -> TransitionInfo -> Bool
/= :: TransitionInfo -> TransitionInfo -> Bool
Eq, Int -> TransitionInfo -> ShowS
[TransitionInfo] -> ShowS
TransitionInfo -> String
(Int -> TransitionInfo -> ShowS)
-> (TransitionInfo -> String)
-> ([TransitionInfo] -> ShowS)
-> Show TransitionInfo
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TransitionInfo -> ShowS
showsPrec :: Int -> TransitionInfo -> ShowS
$cshow :: TransitionInfo -> String
show :: TransitionInfo -> String
$cshowList :: [TransitionInfo] -> ShowS
showList :: [TransitionInfo] -> ShowS
Show)

instance Hashable TransitionInfo where
  hashWithSalt :: Int -> TransitionInfo -> Int
hashWithSalt Int
s (TransitionInfo Offset
off Bool
isDst String
abbr) = Int
s Int -> Offset -> Int
forall a. Hashable a => Int -> a -> Int
`hashWithSalt` Offset
off Int -> Bool -> Int
forall a. Hashable a => Int -> a -> Int
`hashWithSalt` Bool
isDst Int -> String -> Int
forall a. Hashable a => Int -> a -> Int
`hashWithSalt` String
abbr

data TransitionExpression =
    NthDayExpression Int Int Int Int  -- ^ month, nthDay, day, seconds
  | JulianExpression Bool Int Int     -- ^ countLeaps, day, seconds
  deriving (TransitionExpression -> TransitionExpression -> Bool
(TransitionExpression -> TransitionExpression -> Bool)
-> (TransitionExpression -> TransitionExpression -> Bool)
-> Eq TransitionExpression
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TransitionExpression -> TransitionExpression -> Bool
== :: TransitionExpression -> TransitionExpression -> Bool
$c/= :: TransitionExpression -> TransitionExpression -> Bool
/= :: TransitionExpression -> TransitionExpression -> Bool
Eq, Int -> TransitionExpression -> ShowS
[TransitionExpression] -> ShowS
TransitionExpression -> String
(Int -> TransitionExpression -> ShowS)
-> (TransitionExpression -> String)
-> ([TransitionExpression] -> ShowS)
-> Show TransitionExpression
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TransitionExpression -> ShowS
showsPrec :: Int -> TransitionExpression -> ShowS
$cshow :: TransitionExpression -> String
show :: TransitionExpression -> String
$cshowList :: [TransitionExpression] -> ShowS
showList :: [TransitionExpression] -> ShowS
Show)

data TransitionExpressionInfo = TransitionExpressionInfo
  {
     TransitionExpressionInfo -> TransitionExpression
startExpression :: TransitionExpression
    ,TransitionExpressionInfo -> TransitionExpression
endExpression :: TransitionExpression
    ,TransitionExpressionInfo -> TransitionInfo
stdTransInfo :: TransitionInfo
    ,TransitionExpressionInfo -> TransitionInfo
dstTransInfo :: TransitionInfo
  }
  deriving (TransitionExpressionInfo -> TransitionExpressionInfo -> Bool
(TransitionExpressionInfo -> TransitionExpressionInfo -> Bool)
-> (TransitionExpressionInfo -> TransitionExpressionInfo -> Bool)
-> Eq TransitionExpressionInfo
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TransitionExpressionInfo -> TransitionExpressionInfo -> Bool
== :: TransitionExpressionInfo -> TransitionExpressionInfo -> Bool
$c/= :: TransitionExpressionInfo -> TransitionExpressionInfo -> Bool
/= :: TransitionExpressionInfo -> TransitionExpressionInfo -> Bool
Eq, Int -> TransitionExpressionInfo -> ShowS
[TransitionExpressionInfo] -> ShowS
TransitionExpressionInfo -> String
(Int -> TransitionExpressionInfo -> ShowS)
-> (TransitionExpressionInfo -> String)
-> ([TransitionExpressionInfo] -> ShowS)
-> Show TransitionExpressionInfo
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TransitionExpressionInfo -> ShowS
showsPrec :: Int -> TransitionExpressionInfo -> ShowS
$cshow :: TransitionExpressionInfo -> String
show :: TransitionExpressionInfo -> String
$cshowList :: [TransitionExpressionInfo] -> ShowS
showList :: [TransitionExpressionInfo] -> ShowS
Show)

data TransitionInfoOrExp = 
    TransitionInfoFixed TransitionInfo
  | TransitionInfoExpression TransitionExpressionInfo
    deriving (TransitionInfoOrExp -> TransitionInfoOrExp -> Bool
(TransitionInfoOrExp -> TransitionInfoOrExp -> Bool)
-> (TransitionInfoOrExp -> TransitionInfoOrExp -> Bool)
-> Eq TransitionInfoOrExp
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TransitionInfoOrExp -> TransitionInfoOrExp -> Bool
== :: TransitionInfoOrExp -> TransitionInfoOrExp -> Bool
$c/= :: TransitionInfoOrExp -> TransitionInfoOrExp -> Bool
/= :: TransitionInfoOrExp -> TransitionInfoOrExp -> Bool
Eq, Int -> TransitionInfoOrExp -> ShowS
[TransitionInfoOrExp] -> ShowS
TransitionInfoOrExp -> String
(Int -> TransitionInfoOrExp -> ShowS)
-> (TransitionInfoOrExp -> String)
-> ([TransitionInfoOrExp] -> ShowS)
-> Show TransitionInfoOrExp
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TransitionInfoOrExp -> ShowS
showsPrec :: Int -> TransitionInfoOrExp -> ShowS
$cshow :: TransitionInfoOrExp -> String
show :: TransitionInfoOrExp -> String
$cshowList :: [TransitionInfoOrExp] -> ShowS
showList :: [TransitionInfoOrExp] -> ShowS
Show)

-- UTC instant to transition

type UtcTransitionsMap = Map Instant TransitionInfoOrExp

emptyUtcTransitions :: UtcTransitionsMap
emptyUtcTransitions :: UtcTransitionsMap
emptyUtcTransitions = UtcTransitionsMap
forall k a. Map k a
Map.empty

addUtcTransition :: Instant -> TransitionInfo -> UtcTransitionsMap -> UtcTransitionsMap
addUtcTransition :: Instant -> TransitionInfo -> UtcTransitionsMap -> UtcTransitionsMap
addUtcTransition Instant
i TransitionInfo
fti = Instant
-> TransitionInfoOrExp -> UtcTransitionsMap -> UtcTransitionsMap
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Instant
i (TransitionInfo -> TransitionInfoOrExp
TransitionInfoFixed TransitionInfo
fti)

addUtcTransitionExpression :: Instant -> TransitionExpressionInfo -> UtcTransitionsMap -> UtcTransitionsMap
addUtcTransitionExpression :: Instant
-> TransitionExpressionInfo
-> UtcTransitionsMap
-> UtcTransitionsMap
addUtcTransitionExpression Instant
i TransitionExpressionInfo
texp = Instant
-> TransitionInfoOrExp -> UtcTransitionsMap -> UtcTransitionsMap
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Instant
i (TransitionExpressionInfo -> TransitionInfoOrExp
TransitionInfoExpression TransitionExpressionInfo
texp)

activeTransitionFor :: Instant -> TimeZone -> TransitionInfo
activeTransitionFor :: Instant -> TimeZone -> TransitionInfo
activeTransitionFor Instant
i (TimeZone TZIdentifier
_ UtcTransitionsMap
utcM CalDateTransitionsMap
_) = Instant
-> ((Instant, Instant, TransitionInfo, TransitionInfo)
    -> TransitionInfo)
-> (TransitionInfo -> TransitionInfo)
-> TransitionInfoOrExp
-> TransitionInfo
forall a.
Instant
-> ((Instant, Instant, TransitionInfo, TransitionInfo) -> a)
-> (TransitionInfo -> a)
-> TransitionInfoOrExp
-> a
fromTransInfo Instant
i (Instant, Instant, TransitionInfo, TransitionInfo)
-> TransitionInfo
forall {c}. (Instant, Instant, c, c) -> c
f TransitionInfo -> TransitionInfo
forall a. a -> a
id (TransitionInfoOrExp -> TransitionInfo)
-> (Maybe (Instant, TransitionInfoOrExp) -> TransitionInfoOrExp)
-> Maybe (Instant, TransitionInfoOrExp)
-> TransitionInfo
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Instant, TransitionInfoOrExp) -> TransitionInfoOrExp
forall a b. (a, b) -> b
snd ((Instant, TransitionInfoOrExp) -> TransitionInfoOrExp)
-> (Maybe (Instant, TransitionInfoOrExp)
    -> (Instant, TransitionInfoOrExp))
-> Maybe (Instant, TransitionInfoOrExp)
-> TransitionInfoOrExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Instant, TransitionInfoOrExp)
-> Maybe (Instant, TransitionInfoOrExp)
-> (Instant, TransitionInfoOrExp)
forall a. a -> Maybe a -> a
fromMaybe (UtcTransitionsMap -> (Instant, TransitionInfoOrExp)
forall k a. Map k a -> (k, a)
Map.findMin UtcTransitionsMap
utcM) (Maybe (Instant, TransitionInfoOrExp) -> TransitionInfo)
-> Maybe (Instant, TransitionInfoOrExp) -> TransitionInfo
forall a b. (a -> b) -> a -> b
$ Instant
-> UtcTransitionsMap -> Maybe (Instant, TransitionInfoOrExp)
forall k v. Ord k => k -> Map k v -> Maybe (k, v)
Map.lookupLE Instant
i UtcTransitionsMap
utcM     -- NOTE: The findMin case should be impossible
  where
    f :: (Instant, Instant, c, c) -> c
f (Instant
dstStart, Instant
dstEnd, c
stdTI, c
dstTI) = if Instant
i Instant -> Instant -> Bool
forall a. Ord a => a -> a -> Bool
<= Instant
dstStart Bool -> Bool -> Bool
|| Instant
i Instant -> Instant -> Bool
forall a. Ord a => a -> a -> Bool
>= Instant
dstEnd then c
stdTI else c
dstTI

-- CalendarDate to transition

data IntervalEntry a =
    Smallest
  | Entry a
  | Largest
  deriving (IntervalEntry a -> IntervalEntry a -> Bool
(IntervalEntry a -> IntervalEntry a -> Bool)
-> (IntervalEntry a -> IntervalEntry a -> Bool)
-> Eq (IntervalEntry a)
forall a. Eq a => IntervalEntry a -> IntervalEntry a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall a. Eq a => IntervalEntry a -> IntervalEntry a -> Bool
== :: IntervalEntry a -> IntervalEntry a -> Bool
$c/= :: forall a. Eq a => IntervalEntry a -> IntervalEntry a -> Bool
/= :: IntervalEntry a -> IntervalEntry a -> Bool
Eq, Eq (IntervalEntry a)
Eq (IntervalEntry a) =>
(IntervalEntry a -> IntervalEntry a -> Ordering)
-> (IntervalEntry a -> IntervalEntry a -> Bool)
-> (IntervalEntry a -> IntervalEntry a -> Bool)
-> (IntervalEntry a -> IntervalEntry a -> Bool)
-> (IntervalEntry a -> IntervalEntry a -> Bool)
-> (IntervalEntry a -> IntervalEntry a -> IntervalEntry a)
-> (IntervalEntry a -> IntervalEntry a -> IntervalEntry a)
-> Ord (IntervalEntry a)
IntervalEntry a -> IntervalEntry a -> Bool
IntervalEntry a -> IntervalEntry a -> Ordering
IntervalEntry a -> IntervalEntry a -> IntervalEntry a
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall a. Ord a => Eq (IntervalEntry a)
forall a. Ord a => IntervalEntry a -> IntervalEntry a -> Bool
forall a. Ord a => IntervalEntry a -> IntervalEntry a -> Ordering
forall a.
Ord a =>
IntervalEntry a -> IntervalEntry a -> IntervalEntry a
$ccompare :: forall a. Ord a => IntervalEntry a -> IntervalEntry a -> Ordering
compare :: IntervalEntry a -> IntervalEntry a -> Ordering
$c< :: forall a. Ord a => IntervalEntry a -> IntervalEntry a -> Bool
< :: IntervalEntry a -> IntervalEntry a -> Bool
$c<= :: forall a. Ord a => IntervalEntry a -> IntervalEntry a -> Bool
<= :: IntervalEntry a -> IntervalEntry a -> Bool
$c> :: forall a. Ord a => IntervalEntry a -> IntervalEntry a -> Bool
> :: IntervalEntry a -> IntervalEntry a -> Bool
$c>= :: forall a. Ord a => IntervalEntry a -> IntervalEntry a -> Bool
>= :: IntervalEntry a -> IntervalEntry a -> Bool
$cmax :: forall a.
Ord a =>
IntervalEntry a -> IntervalEntry a -> IntervalEntry a
max :: IntervalEntry a -> IntervalEntry a -> IntervalEntry a
$cmin :: forall a.
Ord a =>
IntervalEntry a -> IntervalEntry a -> IntervalEntry a
min :: IntervalEntry a -> IntervalEntry a -> IntervalEntry a
Ord, Int -> IntervalEntry a -> ShowS
[IntervalEntry a] -> ShowS
IntervalEntry a -> String
(Int -> IntervalEntry a -> ShowS)
-> (IntervalEntry a -> String)
-> ([IntervalEntry a] -> ShowS)
-> Show (IntervalEntry a)
forall a. Show a => Int -> IntervalEntry a -> ShowS
forall a. Show a => [IntervalEntry a] -> ShowS
forall a. Show a => IntervalEntry a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall a. Show a => Int -> IntervalEntry a -> ShowS
showsPrec :: Int -> IntervalEntry a -> ShowS
$cshow :: forall a. Show a => IntervalEntry a -> String
show :: IntervalEntry a -> String
$cshowList :: forall a. Show a => [IntervalEntry a] -> ShowS
showList :: [IntervalEntry a] -> ShowS
Show)

type CalDateTransitionsMap = IntervalMap (IntervalEntry Instant) TransitionInfoOrExp

emptyCalDateTransitions :: CalDateTransitionsMap
emptyCalDateTransitions :: CalDateTransitionsMap
emptyCalDateTransitions = CalDateTransitionsMap
forall v a. Ord v => IntervalMap v a
IMap.empty

addCalDateTransition :: IntervalEntry Instant -> IntervalEntry Instant -> TransitionInfo -> CalDateTransitionsMap -> CalDateTransitionsMap
addCalDateTransition :: IntervalEntry Instant
-> IntervalEntry Instant
-> TransitionInfo
-> CalDateTransitionsMap
-> CalDateTransitionsMap
addCalDateTransition IntervalEntry Instant
b IntervalEntry Instant
e TransitionInfo
fti = Interval (IntervalEntry Instant)
-> TransitionInfoOrExp
-> CalDateTransitionsMap
-> CalDateTransitionsMap
forall v a.
Ord v =>
Interval v -> a -> IntervalMap v a -> IntervalMap v a
IMap.insert Interval (IntervalEntry Instant)
interval (TransitionInfo -> TransitionInfoOrExp
TransitionInfoFixed TransitionInfo
fti)
  where
    interval :: Interval (IntervalEntry Instant)
interval = IntervalEntry Instant
-> IntervalEntry Instant -> Interval (IntervalEntry Instant)
forall v. v -> v -> Interval v
Interval IntervalEntry Instant
b IntervalEntry Instant
e

addCalDateTransitionExpression :: IntervalEntry Instant -> IntervalEntry Instant -> TransitionExpressionInfo -> CalDateTransitionsMap -> CalDateTransitionsMap
addCalDateTransitionExpression :: IntervalEntry Instant
-> IntervalEntry Instant
-> TransitionExpressionInfo
-> CalDateTransitionsMap
-> CalDateTransitionsMap
addCalDateTransitionExpression IntervalEntry Instant
b IntervalEntry Instant
e TransitionExpressionInfo
texp = Interval (IntervalEntry Instant)
-> TransitionInfoOrExp
-> CalDateTransitionsMap
-> CalDateTransitionsMap
forall v a.
Ord v =>
Interval v -> a -> IntervalMap v a -> IntervalMap v a
IMap.insert Interval (IntervalEntry Instant)
interval (TransitionExpressionInfo -> TransitionInfoOrExp
TransitionInfoExpression TransitionExpressionInfo
texp)
  where
    interval :: Interval (IntervalEntry Instant)
interval = IntervalEntry Instant
-> IntervalEntry Instant -> Interval (IntervalEntry Instant)
forall v. v -> v -> Interval v
Interval IntervalEntry Instant
b IntervalEntry Instant
e

calDateTransitionsFor :: Instant -> TimeZone -> [TransitionInfo]
calDateTransitionsFor :: Instant -> TimeZone -> [TransitionInfo]
calDateTransitionsFor Instant
i (TimeZone TZIdentifier
_ UtcTransitionsMap
_ CalDateTransitionsMap
cdtMap) = ((Interval (IntervalEntry Instant), TransitionInfoOrExp)
 -> [TransitionInfo])
-> [(Interval (IntervalEntry Instant), TransitionInfoOrExp)]
-> [TransitionInfo]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Instant
-> ((Instant, Instant, TransitionInfo, TransitionInfo)
    -> [TransitionInfo])
-> (TransitionInfo -> [TransitionInfo])
-> TransitionInfoOrExp
-> [TransitionInfo]
forall a.
Instant
-> ((Instant, Instant, TransitionInfo, TransitionInfo) -> a)
-> (TransitionInfo -> a)
-> TransitionInfoOrExp
-> a
fromTransInfo Instant
i (Instant, Instant, TransitionInfo, TransitionInfo)
-> [TransitionInfo]
f (TransitionInfo -> [TransitionInfo] -> [TransitionInfo]
forall a. a -> [a] -> [a]
:[]) (TransitionInfoOrExp -> [TransitionInfo])
-> ((Interval (IntervalEntry Instant), TransitionInfoOrExp)
    -> TransitionInfoOrExp)
-> (Interval (IntervalEntry Instant), TransitionInfoOrExp)
-> [TransitionInfo]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Interval (IntervalEntry Instant), TransitionInfoOrExp)
-> TransitionInfoOrExp
forall a b. (a, b) -> b
snd) ([(Interval (IntervalEntry Instant), TransitionInfoOrExp)]
 -> [TransitionInfo])
-> (CalDateTransitionsMap
    -> [(Interval (IntervalEntry Instant), TransitionInfoOrExp)])
-> CalDateTransitionsMap
-> [TransitionInfo]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CalDateTransitionsMap
-> [(Interval (IntervalEntry Instant), TransitionInfoOrExp)]
forall {a}.
IntervalMap (IntervalEntry Instant) a
-> [(Interval (IntervalEntry Instant), a)]
search (CalDateTransitionsMap -> [TransitionInfo])
-> CalDateTransitionsMap -> [TransitionInfo]
forall a b. (a -> b) -> a -> b
$ CalDateTransitionsMap
cdtMap
  where
    search :: IntervalMap (IntervalEntry Instant) a
-> [(Interval (IntervalEntry Instant), a)]
search = IntervalEntry Instant
-> IntervalMap (IntervalEntry Instant) a
-> [(Interval (IntervalEntry Instant), a)]
forall v a. Ord v => v -> IntervalMap v a -> [(Interval v, a)]
IMap.search (Instant -> IntervalEntry Instant
forall a. a -> IntervalEntry a
Entry Instant
i)
    f :: (Instant, Instant, TransitionInfo, TransitionInfo)
-> [TransitionInfo]
f = ((Interval (IntervalEntry Instant), TransitionInfo)
 -> TransitionInfo)
-> [(Interval (IntervalEntry Instant), TransitionInfo)]
-> [TransitionInfo]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Interval (IntervalEntry Instant), TransitionInfo)
-> TransitionInfo
forall a b. (a, b) -> b
snd ([(Interval (IntervalEntry Instant), TransitionInfo)]
 -> [TransitionInfo])
-> ((Instant, Instant, TransitionInfo, TransitionInfo)
    -> [(Interval (IntervalEntry Instant), TransitionInfo)])
-> (Instant, Instant, TransitionInfo, TransitionInfo)
-> [TransitionInfo]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IntervalMap (IntervalEntry Instant) TransitionInfo
-> [(Interval (IntervalEntry Instant), TransitionInfo)]
forall {a}.
IntervalMap (IntervalEntry Instant) a
-> [(Interval (IntervalEntry Instant), a)]
search (IntervalMap (IntervalEntry Instant) TransitionInfo
 -> [(Interval (IntervalEntry Instant), TransitionInfo)])
-> ((Instant, Instant, TransitionInfo, TransitionInfo)
    -> IntervalMap (IntervalEntry Instant) TransitionInfo)
-> (Instant, Instant, TransitionInfo, TransitionInfo)
-> [(Interval (IntervalEntry Instant), TransitionInfo)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Instant, Instant, TransitionInfo, TransitionInfo)
-> IntervalMap (IntervalEntry Instant) TransitionInfo
buildFixedTransIMap

-- NOTE: Only ever called (from 'resolve') when a local time falls in a spring-forward gap.  This looks partial but is
-- total by construction: every zone's 'CalDateTransitionsMap' tiles [Smallest, Largest] (see the constructors), so the
-- 'IMap.splitAfter' below always yields a non-empty 'front' and 'back' — the 'IMap.bounds'\/'leastView' Nothing cases
-- cannot occur.  A top-level empty search ('go []') only happens in the fixed (historical) region: the expression
-- region always returns a single interval and is handled by 'go [expr]', so the bracketing transitions are always
-- fixed ('bomb' is unreachable).
aroundCalDateTransition :: Instant -> TimeZone -> (TransitionInfo, TransitionInfo)
aroundCalDateTransition :: Instant -> TimeZone -> (TransitionInfo, TransitionInfo)
aroundCalDateTransition Instant
i (TimeZone TZIdentifier
_ UtcTransitionsMap
_ CalDateTransitionsMap
cdtMap) = [TransitionInfoOrExp] -> (TransitionInfo, TransitionInfo)
go ([TransitionInfoOrExp] -> (TransitionInfo, TransitionInfo))
-> (CalDateTransitionsMap -> [TransitionInfoOrExp])
-> CalDateTransitionsMap
-> (TransitionInfo, TransitionInfo)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Interval (IntervalEntry Instant), TransitionInfoOrExp)
 -> TransitionInfoOrExp)
-> [(Interval (IntervalEntry Instant), TransitionInfoOrExp)]
-> [TransitionInfoOrExp]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Interval (IntervalEntry Instant), TransitionInfoOrExp)
-> TransitionInfoOrExp
forall a b. (a, b) -> b
snd ([(Interval (IntervalEntry Instant), TransitionInfoOrExp)]
 -> [TransitionInfoOrExp])
-> (CalDateTransitionsMap
    -> [(Interval (IntervalEntry Instant), TransitionInfoOrExp)])
-> CalDateTransitionsMap
-> [TransitionInfoOrExp]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IntervalEntry Instant
-> CalDateTransitionsMap
-> [(Interval (IntervalEntry Instant), TransitionInfoOrExp)]
forall v a. Ord v => v -> IntervalMap v a -> [(Interval v, a)]
IMap.search (Instant -> IntervalEntry Instant
forall a. a -> IntervalEntry a
Entry Instant
i) (CalDateTransitionsMap -> (TransitionInfo, TransitionInfo))
-> CalDateTransitionsMap -> (TransitionInfo, TransitionInfo)
forall a b. (a -> b) -> a -> b
$ CalDateTransitionsMap
cdtMap
    where
      go :: [TransitionInfoOrExp] -> (TransitionInfo, TransitionInfo)
go [] = (TransitionInfo
before, TransitionInfo
after)
      go [(TransitionInfoExpression (TransitionExpressionInfo TransitionExpression
_ TransitionExpression
_ TransitionInfo
stdTI TransitionInfo
dstTI))] = (TransitionInfo
stdTI, TransitionInfo
dstTI) -- NOTE: Should be the only way this happens
      go [TransitionInfoOrExp]
x = String -> (TransitionInfo, TransitionInfo)
forall a. HasCallStack => String -> a
error (String -> (TransitionInfo, TransitionInfo))
-> String -> (TransitionInfo, TransitionInfo)
forall a b. (a -> b) -> a -> b
$ String
"aroundCalDateTransition: unreachable - a gap search should return [] or a single expression, got: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ [TransitionInfoOrExp] -> String
forall a. Show a => a -> String
show [TransitionInfoOrExp]
x
      before :: TransitionInfo
before = Instant
-> ((Instant, Instant, TransitionInfo, TransitionInfo)
    -> TransitionInfo)
-> (TransitionInfo -> TransitionInfo)
-> TransitionInfoOrExp
-> TransitionInfo
forall a.
Instant
-> ((Instant, Instant, TransitionInfo, TransitionInfo) -> a)
-> (TransitionInfo -> a)
-> TransitionInfoOrExp
-> a
fromTransInfo Instant
i (Instant, Instant, TransitionInfo, TransitionInfo)
-> TransitionInfo
forall {a}. a
bomb TransitionInfo -> TransitionInfo
forall a. a -> a
id (TransitionInfoOrExp -> TransitionInfo)
-> (CalDateTransitionsMap -> TransitionInfoOrExp)
-> CalDateTransitionsMap
-> TransitionInfo
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Interval (IntervalEntry Instant), TransitionInfoOrExp)
-> TransitionInfoOrExp
forall a b. (a, b) -> b
snd ((Interval (IntervalEntry Instant), TransitionInfoOrExp)
 -> TransitionInfoOrExp)
-> (CalDateTransitionsMap
    -> (Interval (IntervalEntry Instant), TransitionInfoOrExp))
-> CalDateTransitionsMap
-> TransitionInfoOrExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Interval (IntervalEntry Instant), TransitionInfoOrExp)]
-> (Interval (IntervalEntry Instant), TransitionInfoOrExp)
forall {a}. [a] -> a
go' ([(Interval (IntervalEntry Instant), TransitionInfoOrExp)]
 -> (Interval (IntervalEntry Instant), TransitionInfoOrExp))
-> (CalDateTransitionsMap
    -> [(Interval (IntervalEntry Instant), TransitionInfoOrExp)])
-> CalDateTransitionsMap
-> (Interval (IntervalEntry Instant), TransitionInfoOrExp)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (IntervalEntry Instant
 -> CalDateTransitionsMap
 -> [(Interval (IntervalEntry Instant), TransitionInfoOrExp)])
-> CalDateTransitionsMap
-> IntervalEntry Instant
-> [(Interval (IntervalEntry Instant), TransitionInfoOrExp)]
forall a b c. (a -> b -> c) -> b -> a -> c
flip IntervalEntry Instant
-> CalDateTransitionsMap
-> [(Interval (IntervalEntry Instant), TransitionInfoOrExp)]
forall v a. Ord v => v -> IntervalMap v a -> [(Interval v, a)]
IMap.search CalDateTransitionsMap
cdtMap (IntervalEntry Instant
 -> [(Interval (IntervalEntry Instant), TransitionInfoOrExp)])
-> (CalDateTransitionsMap -> IntervalEntry Instant)
-> CalDateTransitionsMap
-> [(Interval (IntervalEntry Instant), TransitionInfoOrExp)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Interval (IntervalEntry Instant) -> IntervalEntry Instant
forall v. Interval v -> v
IMap.high (Interval (IntervalEntry Instant) -> IntervalEntry Instant)
-> (CalDateTransitionsMap -> Interval (IntervalEntry Instant))
-> CalDateTransitionsMap
-> IntervalEntry Instant
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Interval (IntervalEntry Instant)
-> Maybe (Interval (IntervalEntry Instant))
-> Interval (IntervalEntry Instant)
forall a. a -> Maybe a -> a
fromMaybe (String -> Interval (IntervalEntry Instant)
forall a. HasCallStack => String -> a
error String
"aroundCalDateTransition: unreachable - empty 'front' (the map always tiles from Smallest)") (Maybe (Interval (IntervalEntry Instant))
 -> Interval (IntervalEntry Instant))
-> (CalDateTransitionsMap
    -> Maybe (Interval (IntervalEntry Instant)))
-> CalDateTransitionsMap
-> Interval (IntervalEntry Instant)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CalDateTransitionsMap -> Maybe (Interval (IntervalEntry Instant))
forall v a. Ord v => IntervalMap v a -> Maybe (Interval v)
IMap.bounds (CalDateTransitionsMap -> TransitionInfo)
-> CalDateTransitionsMap -> TransitionInfo
forall a b. (a -> b) -> a -> b
$ CalDateTransitionsMap
front
      after :: TransitionInfo
after = Instant
-> ((Instant, Instant, TransitionInfo, TransitionInfo)
    -> TransitionInfo)
-> (TransitionInfo -> TransitionInfo)
-> TransitionInfoOrExp
-> TransitionInfo
forall a.
Instant
-> ((Instant, Instant, TransitionInfo, TransitionInfo) -> a)
-> (TransitionInfo -> a)
-> TransitionInfoOrExp
-> a
fromTransInfo Instant
i (Instant, Instant, TransitionInfo, TransitionInfo)
-> TransitionInfo
forall {a}. a
bomb TransitionInfo -> TransitionInfo
forall a. a -> a
id (TransitionInfoOrExp -> TransitionInfo)
-> (CalDateTransitionsMap -> TransitionInfoOrExp)
-> CalDateTransitionsMap
-> TransitionInfo
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Interval (IntervalEntry Instant), TransitionInfoOrExp)
-> TransitionInfoOrExp
forall a b. (a, b) -> b
snd ((Interval (IntervalEntry Instant), TransitionInfoOrExp)
 -> TransitionInfoOrExp)
-> (CalDateTransitionsMap
    -> (Interval (IntervalEntry Instant), TransitionInfoOrExp))
-> CalDateTransitionsMap
-> TransitionInfoOrExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Interval (IntervalEntry Instant), TransitionInfoOrExp),
 CalDateTransitionsMap)
-> (Interval (IntervalEntry Instant), TransitionInfoOrExp)
forall a b. (a, b) -> a
fst (((Interval (IntervalEntry Instant), TransitionInfoOrExp),
  CalDateTransitionsMap)
 -> (Interval (IntervalEntry Instant), TransitionInfoOrExp))
-> (CalDateTransitionsMap
    -> ((Interval (IntervalEntry Instant), TransitionInfoOrExp),
        CalDateTransitionsMap))
-> CalDateTransitionsMap
-> (Interval (IntervalEntry Instant), TransitionInfoOrExp)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Interval (IntervalEntry Instant), TransitionInfoOrExp),
 CalDateTransitionsMap)
-> Maybe
     ((Interval (IntervalEntry Instant), TransitionInfoOrExp),
      CalDateTransitionsMap)
-> ((Interval (IntervalEntry Instant), TransitionInfoOrExp),
    CalDateTransitionsMap)
forall a. a -> Maybe a -> a
fromMaybe (String
-> ((Interval (IntervalEntry Instant), TransitionInfoOrExp),
    CalDateTransitionsMap)
forall a. HasCallStack => String -> a
error String
"aroundCalDateTransition: unreachable - empty 'back' (the map always tiles to Largest)") (Maybe
   ((Interval (IntervalEntry Instant), TransitionInfoOrExp),
    CalDateTransitionsMap)
 -> ((Interval (IntervalEntry Instant), TransitionInfoOrExp),
     CalDateTransitionsMap))
-> (CalDateTransitionsMap
    -> Maybe
         ((Interval (IntervalEntry Instant), TransitionInfoOrExp),
          CalDateTransitionsMap))
-> CalDateTransitionsMap
-> ((Interval (IntervalEntry Instant), TransitionInfoOrExp),
    CalDateTransitionsMap)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CalDateTransitionsMap
-> Maybe
     ((Interval (IntervalEntry Instant), TransitionInfoOrExp),
      CalDateTransitionsMap)
forall v a.
Ord v =>
IntervalMap v a -> Maybe ((Interval v, a), IntervalMap v a)
IMap.leastView (CalDateTransitionsMap -> TransitionInfo)
-> CalDateTransitionsMap -> TransitionInfo
forall a b. (a -> b) -> a -> b
$ CalDateTransitionsMap
back
      (CalDateTransitionsMap
front, CalDateTransitionsMap
back) = IntervalEntry Instant
-> CalDateTransitionsMap
-> (CalDateTransitionsMap, CalDateTransitionsMap)
forall v a.
Ord v =>
v -> IntervalMap v a -> (IntervalMap v a, IntervalMap v a)
IMap.splitAfter (Instant -> IntervalEntry Instant
forall a. a -> IntervalEntry a
Entry Instant
i) CalDateTransitionsMap
cdtMap
      go' :: [a] -> a
go' [] = String -> a
forall a. HasCallStack => String -> a
error String
"aroundCalDateTransition: unreachable - no interval before the gap (the map always tiles from Smallest)"
      go' [a
tei] = a
tei
      go' [a]
_ = String -> a
forall a. HasCallStack => String -> a
error String
"aroundCalDateTransition: unreachable - more than one interval at the boundary before the gap"
      bomb :: a
bomb = String -> a
forall a. HasCallStack => String -> a
error String
"aroundCalDateTransition: unreachable - bracketing transition was an expression, not fixed ('go []' only fires in the fixed region)"

-- | Represents a time zone.  A 'TimeZone' can be used to instanciate a 'ZoneDateTime' from either and 'Instant' or a 'CalendarDateTime'
data TimeZone =
  TimeZone
    {
       TimeZone -> TZIdentifier
zoneName :: TZIdentifier
      ,TimeZone -> UtcTransitionsMap
utcTransitionsMap :: UtcTransitionsMap
      ,TimeZone -> CalDateTransitionsMap
calDateTransitionsMap :: CalDateTransitionsMap
    }

-- | Shows a 'TimeZone' by its identity only (the transition maps are a derived cache, not part of identity).
instance Show TimeZone where
  show :: TimeZone -> String
show TimeZone
tz = case TimeZone -> TZIdentifier
zoneName TimeZone
tz of
    TZIdentifier
UTC    -> String
"<TimeZone UTC>"
    Zone String
n -> String
"<TimeZone " String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall a. Show a => a -> String
show String
n String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
">"

-- | Two 'TimeZone's are equal when they denote the same zone (compared by identifier).  The transition maps are a
--   derived lookup cache fully determined by the identifier, so they are not part of the zone's identity.
instance Eq TimeZone where
  TimeZone
a == :: TimeZone -> TimeZone -> Bool
== TimeZone
b = TimeZone -> TZIdentifier
zoneName TimeZone
a TZIdentifier -> TZIdentifier -> Bool
forall a. Eq a => a -> a -> Bool
== TimeZone -> TZIdentifier
zoneName TimeZone
b

instance Hashable TimeZone where
  hashWithSalt :: Int -> TimeZone -> Int
hashWithSalt Int
s = Int -> TZIdentifier -> Int
forall a. Hashable a => Int -> a -> Int
hashWithSalt Int
s (TZIdentifier -> Int)
-> (TimeZone -> TZIdentifier) -> TimeZone -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TimeZone -> TZIdentifier
zoneName

-- NOTE: 'TimeZone' deliberately has no 'NFData' instance.  'calDateTransitionsMap' is an 'IntervalMap' from the
-- 'fingertree' package, which depends only on 'base' and therefore provides no 'NFData' instance to force it.  A
-- partial 'rnf' that forced only the identifier would silently leave the bulk of the value (the transition maps)
-- unevaluated, which would be a misleading 'NFData', so we omit it entirely.  The same reasoning applies to
-- 'ZonedDateTime' and 'OffsetDateTime', which embed a 'TimeZone'.

-- constructors

fixedOffsetZone :: String -> Offset -> (UtcTransitionsMap, CalDateTransitionsMap, TransitionInfo)
fixedOffsetZone :: String
-> Offset
-> (UtcTransitionsMap, CalDateTransitionsMap, TransitionInfo)
fixedOffsetZone String
tzName Offset
offset = (UtcTransitionsMap
utcM, CalDateTransitionsMap
calDateM, TransitionInfo
tInfo)
    where
      utcM :: UtcTransitionsMap
utcM = Instant -> TransitionInfo -> UtcTransitionsMap -> UtcTransitionsMap
addUtcTransition Instant
bigBang TransitionInfo
tInfo UtcTransitionsMap
emptyUtcTransitions
      calDateM :: CalDateTransitionsMap
calDateM = IntervalEntry Instant
-> IntervalEntry Instant
-> TransitionInfo
-> CalDateTransitionsMap
-> CalDateTransitionsMap
addCalDateTransition IntervalEntry Instant
forall a. IntervalEntry a
Smallest IntervalEntry Instant
forall a. IntervalEntry a
Largest TransitionInfo
tInfo CalDateTransitionsMap
emptyCalDateTransitions
      tInfo :: TransitionInfo
tInfo = Offset -> Bool -> String -> TransitionInfo
TransitionInfo Offset
offset Bool
False String
tzName

-- helper functions

fromTransInfo :: Instant -> ((Instant, Instant, TransitionInfo, TransitionInfo) -> a) -> (TransitionInfo -> a) -> TransitionInfoOrExp -> a
fromTransInfo :: forall a.
Instant
-> ((Instant, Instant, TransitionInfo, TransitionInfo) -> a)
-> (TransitionInfo -> a)
-> TransitionInfoOrExp
-> a
fromTransInfo Instant
_ (Instant, Instant, TransitionInfo, TransitionInfo) -> a
_ TransitionInfo -> a
f (TransitionInfoFixed TransitionInfo
ti) = TransitionInfo -> a
f TransitionInfo
ti
fromTransInfo Instant
i (Instant, Instant, TransitionInfo, TransitionInfo) -> a
f TransitionInfo -> a
_ (TransitionInfoExpression (TransitionExpressionInfo TransitionExpression
startExpr TransitionExpression
endExpr TransitionInfo
stdTI TransitionInfo
dstTI)) = (Instant, Instant, TransitionInfo, TransitionInfo) -> a
f (Instant
dstStart, Instant
dstEnd, TransitionInfo
stdTI, TransitionInfo
dstTI)
  where
    dstStart :: Instant
dstStart = Instant -> TransitionExpression -> Instant
expressionToInstant Instant
i TransitionExpression
startExpr
    dstEnd :: Instant
dstEnd = Instant -> TransitionExpression -> Instant
expressionToInstant Instant
i TransitionExpression
endExpr

-- NOTE: We have to store expressions in the year they take effect so this is the first place we can resolve
--       the actual map.  Otherwise we'd have to create two per year
buildFixedTransIMap :: (Instant, Instant, TransitionInfo, TransitionInfo) -> IntervalMap (IntervalEntry Instant) TransitionInfo
buildFixedTransIMap :: (Instant, Instant, TransitionInfo, TransitionInfo)
-> IntervalMap (IntervalEntry Instant) TransitionInfo
buildFixedTransIMap (Instant
start, Instant
end, TransitionInfo
stdTI, TransitionInfo
dstTI) = [(IntervalEntry Instant, IntervalEntry Instant, TransitionInfo)]
-> IntervalMap (IntervalEntry Instant) TransitionInfo
-> IntervalMap (IntervalEntry Instant) TransitionInfo
forall {v} {a}.
Ord v =>
[(v, v, a)] -> IntervalMap v a -> IntervalMap v a
mkMap [(IntervalEntry Instant, IntervalEntry Instant, TransitionInfo)]
entries IntervalMap (IntervalEntry Instant) TransitionInfo
forall a. Monoid a => a
mempty
  where
    mkMap :: [(v, v, a)] -> IntervalMap v a -> IntervalMap v a
mkMap [] IntervalMap v a
m = IntervalMap v a
m
    mkMap ((v
b, v
e, a
ti):[(v, v, a)]
xs) IntervalMap v a
m = [(v, v, a)] -> IntervalMap v a -> IntervalMap v a
mkMap [(v, v, a)]
xs (IntervalMap v a -> IntervalMap v a)
-> IntervalMap v a -> IntervalMap v a
forall a b. (a -> b) -> a -> b
$ v -> v -> a -> IntervalMap v a -> IntervalMap v a
forall {v} {a}.
Ord v =>
v -> v -> a -> IntervalMap v a -> IntervalMap v a
addEntry v
b v
e a
ti IntervalMap v a
m
    addEntry :: v -> v -> a -> IntervalMap v a -> IntervalMap v a
addEntry v
b v
e a
ti = Interval v -> a -> IntervalMap v a -> IntervalMap v a
forall v a.
Ord v =>
Interval v -> a -> IntervalMap v a -> IntervalMap v a
IMap.insert (v -> v -> Interval v
forall v. v -> v -> Interval v
Interval v
b v
e) a
ti
    entries :: [(IntervalEntry Instant, IntervalEntry Instant, TransitionInfo)]
entries = [(IntervalEntry Instant
forall a. IntervalEntry a
Smallest, Instant -> IntervalEntry Instant
forall a. a -> IntervalEntry a
Entry Instant
beforeStart, TransitionInfo
stdTI), (Instant -> IntervalEntry Instant
forall a. a -> IntervalEntry a
Entry Instant
start', Instant -> IntervalEntry Instant
forall a. a -> IntervalEntry a
Entry Instant
beforeEnd, TransitionInfo
dstTI), (Instant -> IntervalEntry Instant
forall a. a -> IntervalEntry a
Entry Instant
end', IntervalEntry Instant
forall a. IntervalEntry a
Largest, TransitionInfo
stdTI)]
    (Instant
start', Instant
beforeStart) = Instant -> TransitionInfo -> TransitionInfo -> (Instant, Instant)
adjust Instant
start TransitionInfo
dstTI TransitionInfo
stdTI
    (Instant
end', Instant
beforeEnd) = Instant -> TransitionInfo -> TransitionInfo -> (Instant, Instant)
adjust Instant
end TransitionInfo
stdTI TransitionInfo
dstTI
    adjust :: Instant -> TransitionInfo -> TransitionInfo -> (Instant, Instant)
adjust Instant
tran TransitionInfo
ti TransitionInfo
prevTI = (Instant
x, Instant
beforeX)
      where
        x :: Instant
x = Offset -> Instant -> Instant
adjustInstant (TransitionInfo -> Offset
tiUtcOffset TransitionInfo
ti) Instant
tran
        beforeX :: Instant
beforeX = (Instant -> Duration -> Instant) -> Duration -> Instant -> Instant
forall a b c. (a -> b -> c) -> b -> a -> c
flip Instant -> Duration -> Instant
minus (Int -> Duration
fromNanoseconds Int
1) (Instant -> Instant) -> (Instant -> Instant) -> Instant -> Instant
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Offset -> Instant -> Instant
adjustInstant (TransitionInfo -> Offset
tiUtcOffset TransitionInfo
prevTI) (Instant -> Instant) -> Instant -> Instant
forall a b. (a -> b) -> a -> b
$ Instant
tran

expressionToInstant :: Instant -> TransitionExpression -> Instant
expressionToInstant :: Instant -> TransitionExpression -> Instant
expressionToInstant Instant
instant = Int -> TransitionExpression -> Instant
yearExpressionToInstant Int
y
  where
    y :: Int
y = let (Int32
yr, Word8
_, Word8
_) = Instant -> (Int32, Word8, Word8)
instantToYearMonthDay Instant
instant in Int32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
yr

yearExpressionToInstant :: Int -> TransitionExpression -> Instant
yearExpressionToInstant :: Int -> TransitionExpression -> Instant
yearExpressionToInstant Int
y = TransitionExpression -> Instant
go
  where
    go :: TransitionExpression -> Instant
go (NthDayExpression Int
m Int
nth Int
day Int
s) = Int32 -> Word32 -> Word32 -> Instant
Instant Int32
days' (Int -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
s) Word32
0
      where
        m' :: Month Gregorian
m' = Int -> Month Gregorian
forall a. Enum a => Int -> a
toEnum Int
m
        d :: Int
d = Int -> Int -> Month Gregorian -> Int -> Int
nthDayToDayOfMonth Int
nth Int
day Month Gregorian
m' Int
y
        days' :: Int32
days' = Int -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Int32) -> Int -> Int32
forall a b. (a -> b) -> a -> b
$ Int -> Month Gregorian -> Int -> Int
yearMonthDayToDays Int
y Month Gregorian
m' Int
d
    go (JulianExpression Bool
countLeaps Int
day Int
s) = Int32 -> Word32 -> Word32 -> Instant
Instant Int32
days' (Int -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
s) Word32
0
      where
        -- POSIX day-of-year transition.  The @n@ form (countLeaps == True) is 0-based and counts 29.Feb; the @Jn@
        -- form (countLeaps == False) is 1-based and never counts 29.Feb, so 1.Mar is always day 60.  NOTE: this is a
        -- day-of-year, unrelated to the Julian *calendar*.
        offset :: Int
offset
          | Bool
countLeaps          = Int
day
          | Bool
isLeap Bool -> Bool -> Bool
&& Int
day Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
60  = Int
day
          | Bool
otherwise            = Int
day Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
        isLeap :: Bool
isLeap = Month Gregorian -> Int -> Int
maxDaysInMonth (Int -> Month Gregorian
forall a. Enum a => Int -> a
toEnum Int
1) Int
y Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
29
        days' :: Int32
days' = Int -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Int32) -> Int -> Int32
forall a b. (a -> b) -> a -> b
$ Int -> Month Gregorian -> Int -> Int
yearMonthDayToDays Int
y (Int -> Month Gregorian
forall a. Enum a => Int -> a
toEnum Int
0) Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
offset