There may be various approaches to solving this, like a recursive CTE (if you are on '2005+ version), a numbers table, a calendar table (which may also be considered as special case numbers table method).
The following example uses a system table called master..spt_values
acting as a plain old numbers table:
WITH sampledata (ID, FK, Location, StartDateTime, EndDateTime) AS (
SELECT 1, 254, 'Loc1', CAST('20110910 10:23' AS datetime), CAST('20110910 12:45' AS datetime) UNION ALL
SELECT 2, 254, 'Loc2', CAST('20110910 12:45' AS datetime), CAST('20110910 12:50' AS datetime)
),
hourly AS (
SELECT
d.ID,
d.FK,
d.Location,
d.StartDateTime,
d.EndDateTime,
HourlyStartDateTime = DATEADD(HOUR, DATEDIFF(HOUR, 0, d.StartDateTime) + v.number, 0),
HourlyEndDateTime = DATEADD(MS, -3, DATEADD(HOUR, DATEDIFF(HOUR, 0, d.StartDateTime) + v.number + 1, 0))
FROM sampledata d
INNER JOIN master..spt_values v ON v.type = 'P'
AND v.number BETWEEN 0 AND DATEDIFF(HOUR, d.StartDateTime, d.EndDateTime)
)
SELECT
ID,
FK,
Location,
StartDateTime = CASE WHEN StartDateTime < HourlyStartDateTime THEN HourlyStartDateTime ELSE StartDateTime END,
EndDateTime = CASE WHEN EndDateTime > HourlyEndDateTime THEN HourlyEndDateTime ELSE EndDateTime END
FROM hourly
Because I'm not sure about the time granulation in your data, I defined HourlyEndDateTime
as "3 milliseconds to the end of the current hour". You may well change it to "1 second or 1 minute to the end of the hour".