There are a few different patterns available when working with time. You seem to currently be using Timeslots but this indeed limits tasks and leads to some wasted time (since a task always has to take up the full timeslot).
On this page, you can read about 2 alternative approaches: Timegrain and Chained Through Time.
The task allocation quickstart on GitHub uses Chained Through Time. It seems to match most of your requirements.