You will need a numbers or calendar table to make things easy, or we can simulate one if the range is small. Is the TaskDate a plain date, or does it have a time component also?
Basic plan of attack is:
declare @StartDate datetime
declare @EndDate datetime
/* Set @StartDate and @EndDate to represent the range */
with Digits as (
select 0 as d union all select 1 union all select 2 union all select 3 union all select 4 union all
select 5 union all select 6 union all select 7 union all select 8 union all select 9
), Numbers as (
select (D1.d * 100) + (D2.d * 10) + D3.d as n
from Digits D1,Digits D2,Digits D3
), TaskDates as (
select
t.TaskID,
DATEADD(day,n.n,@StartDate) as TaskDate
from
Tasks t
inner join
Numbers n
on
DATEADD(day,n.n,@StartDate) <= @EndDate
)
select
*
from
TaskDates td1
left join
TasksDone td2
on
td1.TaskID = td2.TaskID and
DATEDIFF(day,td1.TaskDate,td2.TaskDate) = 0
where
td2.TaskID is null
The first two CTEs build a small numbers table, the 3rd CTE constructs a set of TaskIDs and Dates within the required range. The final select matches theses against the TasksDone table, and then discards those rows where a match is found. If TasksDone.TaskDate is a plain date (no time component) and @StartDate is also with no time component, then you can ditch the DATEDIFF and just use td1.TaskDate = td2.TaskDate.
If you need a large range (above can cover ~3 years), I'd suggest building a proper number table or calendar table