Nominal axes cannot have scale-bound selections (i.e. cannot be "zoomed"). Try using a temporal encoding instead:
alt.Chart(source).mark_bar().encode(
x='start:T',
x2='end:T',
y='task'
).interactive()
If you want the y axis to be interactive, this is more difficult because Vega-Lite does not support panning and zooming of nominal axes, and nominal is the only appropriate encoding for string categories.
But you can get around this with the pretty terrible hack of mapping your data to numbers, then using a quantitative bin transform and re-mapping the numbers back to axis labels. For example:
import altair as alt
import pandas as pd
import numpy as np
source = pd.DataFrame([
{"task": "A", "start": '2021-12-03 11:00:00', "end": '2021-12-03 12:00:00'},
{"task": "B", "start": '2021-12-03 13:45:00', "end": '2021-12-03 15:30:00'},
{"task": "C", "start": '2021-12-03 19:20:00', "end": '2021-12-03 20:45:00'}
])
source['tasknum'] = [0, 1, 2]
alt.Chart(source).mark_bar().encode(
x='start:T',
x2='end:T',
y=alt.Y('tasknum:Q',
bin=alt.Bin(extent=[-0.5, 2.5], step=1.0, nice=False),
axis=alt.Axis(values=[0, 1, 2],
labelExpr="{0:'A', 1:'B', 2:'C'}[datum.value]"))
).properties(
height=150
).interactive()
To be quite honest, though, I wouldn't actually do this, and instead I'd just live with the limitation that nominal axes cannot be zoomed or panned, or (if this is not sufficient) find another plotting package that does what you want.