module FuzzyTimings.TimeSlice (TimeSlice(..), tsDuration, mkTimeSlice, overlaps, addSecs, inTimeSlice, intersectTimeSlice, deleteTimeSlice, cutTimeSlice) where import Data.Time.LocalTime import Data.Time.Calendar import Data.Time.Clock import Data.Maybe import Data.List data TimeSlice a = TimeSlice { tsStart :: LocalTime, tsEnd :: LocalTime, tsValue :: a } deriving (Show) instance Eq (TimeSlice a) where t1 == t2 = tsStart t1 == tsStart t2 && tsEnd t1 == tsEnd t2 instance Ord (TimeSlice a) where compare t1 t2 = compare (tsStart t1) (tsStart t2) tsDuration :: TimeSlice a -> Int tsDuration t = floor $ (localTimeToUTC utc $ tsEnd t) `diffUTCTime` (localTimeToUTC utc $ tsStart t) addSecs :: Int -> LocalTime -> LocalTime addSecs s lt = LocalTime { localDay = addDays (fromIntegral (days + extraDay)) (localDay lt), localTimeOfDay = dayFractionToTimeOfDay endTodFrac } where days = s `div` 86400 rest = (fromIntegral (s `mod` 86400)) / 86400 tod = localTimeOfDay lt endTodFrac' = (timeOfDayToDayFraction tod) + rest (extraDay, endTodFrac) | endTodFrac' >= 1 = (1, endTodFrac' - 1) | otherwise = (0, endTodFrac') mkTimeSlice :: LocalTime -> Int -> a -> TimeSlice a mkTimeSlice start duration value = TimeSlice { tsStart = start, tsEnd = addSecs duration start, tsValue = value } inTimeSlice :: TimeSlice a -> LocalTime -> Bool inTimeSlice ts lt = lt >= tsStart ts && lt < tsEnd ts overlaps :: TimeSlice a -> TimeSlice b -> Bool overlaps t1 t2 | tsStart t1 == tsEnd t1 = False | tsStart t2 == tsEnd t2 = False | otherwise = (tsStart t1 >= tsStart t2 && tsStart t1 < tsEnd t2) || (tsStart t2 >= tsStart t1 && tsStart t2 < tsEnd t1) intersectTimeSlice :: TimeSlice a -> TimeSlice b -> Maybe (TimeSlice a) intersectTimeSlice t1 t2 | overlaps t1 t2 = Just (t1 { tsStart = max (tsStart t1) (tsStart t2), tsEnd = min (tsEnd t1) (tsEnd t2) }) | otherwise = Nothing deleteTimeSlice :: TimeSlice a -> TimeSlice b -> [TimeSlice a] deleteTimeSlice t1 t2 = deleteBy $ intersectTimeSlice t1 t2 where deleteBy (Just t3) | tsStart t3 > tsStart t1 && tsEnd t3 < tsEnd t1 = [ t1 { tsEnd = tsStart t3 }, t1 { tsStart = tsEnd t3 } ] | tsStart t3 > tsStart t1 = [ t1 { tsEnd = tsStart t3 }] | otherwise = [ t1 { tsStart = tsEnd t3 } ] deleteBy Nothing = [t1] cutTimeSlice :: [LocalTime] -> TimeSlice a -> [TimeSlice a] cutTimeSlice times ts = let times' = tsStart ts : filter (inTimeSlice ts) times ++ [tsEnd ts] cut (t1:t2:lts) = ts { tsStart = t1, tsEnd = t2 }:cut (t2:lts) cut _ = [] in (cut . sort . nub) times'