Here's an instead of trigger that I think handles all scenarios.
CREATE TRIGGER dbo.PreventOverlappingBookings
ON dbo.BOOKINGS INSTEAD OF INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (
SELECT 1 FROM inserted AS i
INNER JOIN dbo.BOOKINGS AS b
ON (b.id <> i.id OR i.id = 0) -- 0 for insert
AND b.apartman_id = i.apartman_id
AND ((b.start_date <= i.end_date AND b.end_date >= i.start_date)
OR (b.start_date <= i.start_date AND b.end_date >= i.end_date)
OR (b.end_date <= i.start_date AND b.end_date >= i.end_date))
) OR EXISTS (
-- also make sure there are no overlaps in a set-based insert/update
SELECT 1 FROM inserted AS i
INNER JOIN inserted AS b
ON (b.id <> i.id OR i.id = 0) -- 0 for insert
AND b.apartman_id = i.apartman_id
AND ((b.start_date <= i.end_date AND b.end_date >= i.start_date)
OR (b.start_date <= i.start_date AND b.end_date >= i.end_date)
OR (b.end_date <= i.start_date AND b.end_date >= i.end_date))
)
BEGIN
RAISERROR('Overlapping date range.', 11, 1);
END
ELSE
BEGIN
UPDATE b SET start_date = i.start_date, end_date = i.end_date
FROM dbo.BOOKINGS AS b
INNER JOIN inserted AS i
ON b.id = i.id;
IF @@ROWCOUNT > 0
BEGIN
INSERT dbo.BOOKINGS(start_date, end_date, apartman_id)
SELECT start_date, end_date, apartman_id FROM inserted AS i;
END
END
END
GO
Some answers will suggest a function in a UDF, but I don't trust them (and neither should you, IMHO).