Use two recursions. One is for the dates and the other is for the hours. And then use CROSS APPLY:
DECLARE @startDate date = '2021-01-01';
DECLARE @endDate date = '2031-01-01';
;WITH CTE_Date AS (
SELECT @startDate AS [Date]
UNION ALL
SELECT DATEADD(DAY, 1, [Date]) AS [Date]
FROM CTE_Date
WHERE [Date] < @endDate
),
CTE_Hour AS (
SELECT 0 AS [Hour]
UNION ALL
SELECT [Hour] + 1 AS [Hour]
FROM CTE_Hour
WHERE [Hour] < 23
)
SELECT YEAR(d.[Date]) AS [Year],
RIGHT('0' + CAST(MONTH(d.[Date]) AS varchar(2)), 2) AS [Month],
RIGHT('0' + CAST(DAY(d.[Date]) AS varchar(2)), 2) AS [Day],
RIGHT('0' + CAST(h.[Hour] AS varchar(2)), 2) AS [Hour]
FROM CTE_Date AS d
CROSS APPLY (
SELECT [Hour] FROM CTE_Hour
) AS h
ORDER BY [Year], [Month], [Day], h.[Hour]
OPTION(MAXRECURSION 0);
GO