@@ -1,85 +0,0 @@
1−module Data.Time.FastSeries
2− ( TimeZoneSeries
3− , toTimeZoneSeries
4− , fromTimeZoneSeries
5− , timeZoneFromSeries
6− , timeZoneFromSeriesLocal
7− , utcToLocalTime'
8− , localTimeToUTC'
9− ) where
10−
11−import Data.Map.Strict (Map)
12−import qualified Data.Map as Map
13−import Data.Time
14−import qualified Data.Time.LocalTime.TimeZone.Series as Series
15−
16−
17−-- | Contains the same data as 'Series.TimeZoneSeries' but precomputes some data
18−-- for faster conversion.
19−data TimeZoneSeries = TimeZoneSeries
20− { fallback :: !TimeZone
21− , transitionsUTC :: !(Map UTCTime TimeZone)
22− -- ^ UTC -> Local wants to know the last transition that happened before a
23− -- given 'UTCTime'
24− , transitionsLocal :: !(Map LocalTime TimeZone)
25− -- ^ Local -> UTC wants to know the largest transition @t@ into timezone
26− -- @tz@ such that @t <= localTimeToUTC tz lt@ where @lt@ is a given
27− -- 'LocalTime'. For a given TZ and valid time arguments, 'localTimeToUTC'
28− -- and 'utcToLocalTime' are inverses. Hence this is equivalent to
29− -- @utcToLocalTime t tz <= lt@.
30− }
31−
32−toTimeZoneSeries :: Series.TimeZoneSeries -> TimeZoneSeries
33−toTimeZoneSeries
34− Series.TimeZoneSeries { Series.tzsTimeZone, Series.tzsTransitions }
35− = TimeZoneSeries
36− { fallback = tzsTimeZone
37− , transitionsUTC = Map.fromDescList tzsTransitions
38− , transitionsLocal = Map.fromList
39− [(utcToLocalTime tz u, tz) | (u, tz) <- tzsTransitions]
40− }
41−
42−fromTimeZoneSeries :: TimeZoneSeries -> Series.TimeZoneSeries
43−fromTimeZoneSeries TimeZoneSeries { fallback, transitionsUTC }
44− = Series.TimeZoneSeries
45− { Series.tzsTimeZone = fallback
46− , Series.tzsTransitions = Map.toList transitionsUTC
47− }
48−
49−timeZoneFromSeries :: TimeZoneSeries -> UTCTime -> TimeZone
50−timeZoneFromSeries TimeZoneSeries { fallback, transitionsUTC } t
51− = maybe fallback snd $ Map.lookupLE t transitionsUTC
52−
53−-- | See caveat on 'localTimeToUTC''.
54−timeZoneFromSeriesLocal :: TimeZoneSeries -> LocalTime -> TimeZone
55−timeZoneFromSeriesLocal TimeZoneSeries { fallback, transitionsLocal } lt
56− = maybe fallback snd $ Map.lookupLE lt transitionsLocal
57−
58−utcToLocalTime' :: TimeZoneSeries -> UTCTime -> LocalTime
59−utcToLocalTime' tzsf t = utcToLocalTime tz t
60− where !tz = timeZoneFromSeries tzsf t
61−
62−-- | This conversion is not actually always well defined.
63−--
64−-- At the moment of switching to summer time, there is a range of 'LocalTime's
65−-- that are skipped. For these, 'Series.isValidLocalTime' returns 'False'. This
66−-- function converts them to 'UTCTime' as if the switch didn't happen.
67−--
68−-- Around the moment of switching away from summer time, there is a range of
69−-- 'LocalTime's that happen twice: first before the switch, then once again
70−-- after the switch. For these, 'Series.isRedundantLocalTime' returns 'True'.
71−-- In such ambiguous cases, this function always picks the occurrence after the
72−-- switch.
73−--
74−-- The handling of these corner cases matches exactly to that of
75−-- 'Series.localTimeToUTC''
76−--
77−-- This means the equation
78−-- @lt == utcToLocalTime' tzsf (localTimeToUTC' tzsf lt)@ does not hold for @lt@
79−-- that were skipped. And the equation
80−-- @t == localTimeToUTC' tzsf (utcToLocalTime' tzsf t)@ does not hold for @t@
81−-- that come just before switching awya from summer time. For other arguments
82−-- these equations are assumed to hold.
83−localTimeToUTC' :: TimeZoneSeries -> LocalTime -> UTCTime
84−localTimeToUTC' tzsf lt = localTimeToUTC tz lt
85− where !tz = timeZoneFromSeriesLocal tzsf lt