I don't think this is possible without the use of a temporary table. (You don't exclude their use, but you didn't exclude using cursors either until Mike Bennett had posted his answer)
I'm reasonably confident that this is a generic solution - it uses an undocumented feature where it's possible to change the value of a variable more than once during an update statement.
You may be able to skip the creation of an artificial identity column to guarantee order (autoID in my query) if the records in your table are entered in order of effdate.
-- Setup test data
IF object_id('tempdb..#test1') IS NOT NULL
DROP TABLE #test1
GO
CREATE TABLE #test1
(id INT
,effdate DATETIME
,termdate DATETIME
)
INSERT #test1
SELECT 1,'2007-05-01','2007-05-31'
UNION SELECT 2 ,'2007-06-01','2007-06-30'
UNION SELECT 3 ,'2007-07-01','2007-09-30'
UNION SELECT 4 ,'2008-03-01','2008-03-31'
UNION SELECT 5 ,'2008-05-01','2008-05-31'
UNION SELECT 6 ,'2008-06-01','2008-06-30'
GO
IF object_id('tempdb..#t') IS NOT NULL
DROP TABLE #t
GO
-- Order the records by effdate
SELECT IDENTITY(INT,1,1) AS autoId
,cast(NULL AS INT) groupID
,*
INTO #t
FROM #test1
ORDER BY effdate
UPDATE #t
SET groupID = 1
WHERE autoID = 1
DECLARE @gp INT
SET @gp = 1
--update groupID using the undocumented variable-update method
UPDATE t2
SET @gp = CASE WHEN t1.termdate = t2.effdate - 1
THEN @gp
ELSE @gp + 1
END
,groupID = @gp
FROM #t AS t1
JOIN #t AS t2
ON t1.autoID = t2.autoID - 1
--output results
select min(effdate), max(termdate)
from #t
group by groupID
order by groupID