To solve your issue, I did the following (a fiddle for all code below is available here):
A couple of points:
This solution makes use of the LAG() window function.
Window functions are very powerful and will repay any effort spent learning them many times over.
And finally, PostgreSQL 9.5 is no longer supported - you might be better off moving to a supported version.
First step:
We obtain the points at which a change occurs in either colony or protection.
SELECT
colony,
protection,
date_check,
CASE
WHEN (LAG(protection, 1) OVER (PARTITION BY colony
ORDER BY date_check) != protection) THEN 1
ELSE 0
END AS change
FROM test;
Result (snipped for brevity):
colony protection date_check change
2 N 2019-10-26 0
2 N 2019-10-27 0
2 Y 2019-11-01 1
2 Y 2019-11-03 0
7 Y 2019-10-12 0
7 Y 2019-10-13 0
...
...
Step 2:
We sum the changes, obtaining distinct records for each start and stop date of when protection = 'Y'.
SELECT
colony,
date_check,
SUM(change) OVER (PARTITION BY colony ORDER BY date_check) AS sc
FROM
(
SELECT
colony,
protection,
date_check,
CASE
WHEN (LAG(protection, 1) OVER (PARTITION BY colony
ORDER BY date_check) != protection) THEN 1
ELSE 0
END AS change
FROM test
) AS tab
WHERE protection = 'Y' -- test what happens when we comment out this line...
ORDER BY colony, date_check;
Result:
colony date_check sc
2 2019-11-01 1
2 2019-11-03 1
7 2019-10-12 0
7 2019-10-13 0
7 2019-10-14 0
7 2019-10-15 0
7 2019-10-16 0
7 2019-10-17 0
7 2019-10-23 1
7 2019-10-24 1
7 2019-10-25 1
7 2019-10-26 1
7 2019-11-01 2
7 2019-11-04 2
Step 3:
Finally, we get the MIN() and MAX() of the check_date
where there has been a change from 'N' to 'Y' (or vice versa) but only taking those where protection
= 'Y'.
SELECT
colony,
MIN(date_check) AS "Date in",
MAX(date_check) AS "Date out"
, sc -- not strictly necessary here in the SELECT - illustrative!
FROM
(
SELECT
colony,
date_check,
SUM(change) OVER (PARTITION BY colony ORDER BY date_check) AS sc
FROM
(
SELECT
colony,
protection,
date_check,
CASE
WHEN (LAG(protection, 1) OVER (PARTITION BY colony
ORDER BY date_check) != protection) THEN 1
ELSE 0
END AS change
FROM test
) AS tab1
WHERE protection = 'Y'
ORDER BY colony, date_check
) AS tab2
GROUP BY colony, sc -- sc not necessary in the SELECT but
ORDER BY colony, sc; -- it is required in the GROUP BY - test!!!
Result:
colony Date in Date out sc
2 2019-11-01 2019-11-03 1
7 2019-10-12 2019-10-17 0
7 2019-10-23 2019-10-26 1
7 2019-11-01 2019-11-04 2
Q.E.D.
The window function solution appears to be the most performant - see here - run the fiddle several times and vary the order of the queries... I've tried to warm up the cache before running EXPLAIN (ANALYZE...)
but test with your own tables(s) and hardware...