visie-0.6.0.0: src/Visie/ToTimeSeries.hs
module Visie.ToTimeSeries where
import Data.List (sortOn)
import Data.Time.Clock (NominalDiffTime,addUTCTime)
import Data.Ord (compare)
import Data.Time (UTCTime)
data Timestamped a = Timestamped { getStamped :: a, getTime :: UTCTime }
fill :: Monoid a => UTCTime -> Timestamped a
fill t = Timestamped mempty t
-- | sort `elements` concatenating their monoid when their time falls
-- within the same interval
convert :: Monoid a => NominalDiffTime -> [Timestamped a] -> [Timestamped a]
convert interval elements = sampler sorted
where sorted = sortOn getTime elements
times = iterator interval (getTime (head sorted))
sampler = consume times
iterator :: NominalDiffTime -> UTCTime -> [UTCTime]
iterator interval start = iterate (addUTCTime interval) start
-- at every call, the recursive function returns a processed list, and
-- it gets a non processed list and a reference date about the last
-- emitted element. if the next element would have a date greater than
-- the reference + the interval, a filling element is
-- created. otherwise, the function will look ahead and merge all
-- elements within the same interval, pick a representative date for
-- the merged elements and use it as the new reference
consume :: Monoid a => [UTCTime] -> [Timestamped a] -> [Timestamped a]
consume (t:ts) [] = []
consume (t:ts) elements
| length preceding == 0 = filled : rest
| otherwise = foldl (merge t) filled preceding : rest
where (preceding, succeeding) = span ((<= t) . getTime) elements
filled = fill t
rest = consume ts succeeding
merge :: Monoid a => UTCTime -> Timestamped a -> Timestamped a -> Timestamped a
merge t (Timestamped a _) (Timestamped b _) = Timestamped (mappend a b) t
convertFill :: Monoid a => UTCTime -> NominalDiffTime -> [Timestamped a] -> [Timestamped a]
convertFill dateTime interval elements = convert interval filledElements
where filledElements = (fill dateTime):elements