83 lines
2.9 KiB
Haskell
83 lines
2.9 KiB
Haskell
-- | Tasks and events can have one or multiple deadlines.
|
|
--
|
|
-- This module contains a representation for single deadlines,
|
|
-- and some useful functions for calculating things with them.
|
|
|
|
module TaskMachine.Deadline
|
|
( Deadline(..)
|
|
, Duration
|
|
-- * Calculations
|
|
, relevant
|
|
, isRelevant
|
|
, nextDeadlines
|
|
, relevant'
|
|
, isRelevant'
|
|
, nextDeadlines'
|
|
) where
|
|
|
|
import Data.Maybe
|
|
|
|
import Data.Time.Calendar
|
|
|
|
import qualified TaskMachine.DateExpr as TM
|
|
|
|
-- | Duration of a task or event in days.
|
|
--
|
|
-- A duration of 1 means one day.
|
|
-- Because of this, 'Duration' values __must always be greater than 0__.
|
|
type Duration = Int
|
|
|
|
-- | A way to represent a deadline, either as a fixed date or using a formula.
|
|
data Deadline
|
|
= DFixed Day (Maybe Duration)
|
|
| DExpression TM.BoolExpr Duration
|
|
|
|
-- | Find the next date of the 'Deadline' that's important for a certain day.
|
|
-- This returns a @Just@ when the day lies within the duration specified.
|
|
--
|
|
-- If no duration is specified in a 'DFixed' deadline, all days before the deadline,
|
|
-- including the deadline itself, are important (i. e. the duration is infinite).
|
|
relevant :: Day -> Deadline -> Maybe Day
|
|
relevant today (DExpression expr duration) = TM.findNext today duration expr
|
|
relevant today (DFixed day Nothing)
|
|
| diffDays day today >= 0 = Just day
|
|
| otherwise = Nothing
|
|
relevant today (DFixed day (Just duration))
|
|
| diff >= 0 && diff < toInteger duration = Just day
|
|
| otherwise = Nothing
|
|
where diff = diffDays day today
|
|
|
|
-- | A version of 'relevant' modified to take a list of Deadlines.
|
|
relevant' :: Day -> [Deadline] -> Maybe Day
|
|
relevant' today deadlines =
|
|
let relevants = mapMaybe (relevant today) deadlines
|
|
in case relevants of
|
|
[] -> Nothing
|
|
days -> Just $ minimum days
|
|
|
|
|
|
|
|
-- | Whether the 'Deadline' is relevant on the current day or not.
|
|
--
|
|
-- This function works like 'relevant', only that the actual date calculated is irrelevant.
|
|
--
|
|
-- @'isRelevant' day = 'isJust' . 'relevant' day@
|
|
isRelevant :: Day -> Deadline -> Bool
|
|
isRelevant day = isJust . relevant day -- Hey, this even reads like English! :D
|
|
|
|
-- | A version of 'isRelevant' modified to take a list of Deadlines.
|
|
isRelevant' :: Day -> [Deadline] -> Bool
|
|
isRelevant' day = any (isRelevant day)
|
|
|
|
-- | Calculate all occurrences of this deadline within the duration given.
|
|
nextDeadlines :: Day -> Duration -> Deadline -> [Day]
|
|
nextDeadlines start duration (DFixed day _)
|
|
| diff >= 0 && diff < toInteger duration = [day]
|
|
| otherwise = []
|
|
where diff = diffDays day start
|
|
nextDeadlines start duration (DExpression expr _) =
|
|
TM.findWithin start duration expr
|
|
|
|
-- | A version of 'nextDeadlines' modified to take a list of Deadlines.
|
|
nextDeadlines' :: Day -> Duration -> [Deadline] -> [Day]
|
|
nextDeadlines' start duration = concatMap (nextDeadlines start duration)
|