Using a date for a datetime field in a SOQL Query criteria
You'll want to check out the Date Literals that were added a few releases ago. They start at midnight of each range, and have things like LAST_N_DAYS, LAST_QUARTER, etc.
The only gotcha is it takes a little to get used to operators for ranges; = means "in", < means "before the beginning of", and > means "after the end of".
Update
Borrowing from @highfive's answer, the easiest way to do this is use the DAY_ONLY
operator to convert the DateTime.
SELECT Id FROM Case WHERE DAY_ONLY(ClosedDate) >= :startOfMonth
AND DAY_ONLY(ClosedDate) <= :endOfMonth
Old Answer
So it seems like there are four options. I'd probably go for the last one, which seems the clearest and simplest. In hindsight this question appears subjective and probably not right for the forum =.
First, you can use @sathya's suggestion which works but might be counter-intuitive if you don't understand the date to datetime conversion that's happening.
Date startOfMonth = Date.newInstance(2012,9,1);
Date endOfMonth = Date.newInstance(2012,10,1);
List<Case> cases = [
select id from Case
where closedDate >= :startOfMonth
and closedDate <= :endOfMonth];
Second, you can exactly specify the date time window. Which works but feels a little verbose.
DateTime startOfMonthDT = DateTime.newInstance(2012,9,1,0,0,0);
DateTime endOfMonthDT = DateTime.newInstance(2012,9,30,23,59,59);
List<Case> cases = [
select id from Case
where closedDate >= :startOfMonthDT
and closedDate <= :endOfMonthDT];
Third, if you're only worrying about the current or last month you can use date literals.
List<Case> cases = [
select id from Case
where closedDate = LAST_MONTH];
Finally, you can use the date functions, which seems like the clearest (to me at least)
Integer month = 9;
Integer year = 2012;
List<Case> cases = [
select id from Case
where CALENDAR_MONTH(closedDate) = :month
and CALENDAR_YEAR(closedDate) = :year];
It is worth noting that Apex performs auto-conversion between Date and DateTime, capturing the UTC/GMT timezone date.
Date first = Date.newInstance(2018, 03, 19);
DateTime firstDT = first; // this is 2018/03/19 00:00:00.0
DateTime secondDT = DateTime.newInstance(2018, 03, 19, 13, 37, 06);
Date second = secondDT; // this is 2018/03/19
This happens for assignments, like above, and when passing parameters (because we are effectively assigning a value to a parameter variable anyway). Explicit casts can also be used though are unnecessary.
BTW, I would suggest that the first example in Ralph Callaway's response isn't quite right. I think this should be changed as follows:
Date startOfMonth = Date.newInstance(2012,9,1);
Date startOfNextMonth = Date.newInstance(2012,10,1);
List<Case> cases = [
select id from Case
where closedDate >= :startOfMonth
and closedDate < :startOfNextMonth];
The change is from "closedDate <= :endOfMonth" to "closedDate < :startOfNextMonth". We don't want an entry falling at exactly midnight at the start of the next month being matched, hence the "<" instead of "<=".