Creating index on TIMESTAMP column to use with range operators
The name "foo_date" indicates a date and is a bad choice for a timestamp column. Option 1 would work just fine with actual dates.
A plain btree index in combination with Option 2 is the unequivocally best solution. Look no further. Except maybe for the special case of a BRIN index for large tables with physically sorted data. See:


Speed up creation of Postgres partial index



Proof of concept

That said, to make a GiST or SP-GiST index work, you could create an expression index on fake ranges. You do not need the module btree_gist for this. Demonstrating with SP-GiST since that is typically a bit faster here. See:


Perform this hours of operation query in PostgreSQL




CREATE INDEX foo_date_spgist_idx ON foo USING spgist(tsrange(foo_date, foo_date, '[]'));

SELECT * FROM foo
WHERE  tsrange(foo_date, foo_date, '[]') &lt;@ tsrange('2018-01-01', '2018-02-01')


Or with range literal:

...
WHERE  tsrange(foo_date, foo_date, '[]') &lt;@ '[2018-01-01,2018-02-01)'


But: bigger, more expensive to maintain, slower than the btree index. Not even less cumbersome to write. Pointless for your case.

Aside: technically, you could:

... WHERE foo_date BETWEEN '2018-01-01' AND '2018-01-31 23:59.999999';


The Postgres timestamp type is (currently) implemented with µs resolution, i.e. max. 6 fractional digits. Hence the expression does exactly what you want. But I strongly advice against building on this implementation detail. Option 2 is the way to go. Related:


Interval (days) in PostgreSQL with two parameters
Ignoring time zones altogether in Rails and PostgreSQL