Precision-based approach (PHP 7.1+):
If you don't want the ambiguity of string-based manipulations, here's an alternative using DateTime
with microsecond-precision:
$query_date = '2010-02-04';
$datetime = DateTime::createFromFormat('Y-m-d', $query_date); // create DateTime obj from 'Y-m-d'
$first_day_of_month = (clone $datetime) // duplicate; don't touch original object
->setDate( $datetime->format('Y'), $datetime->format('m'), 1 ) // set to 1st of month
->setTime( 0, 0, 0, 0 ); // reset time to 00:00:00.000000
$last_day_of_month = (clone $datetime)
->add( new DateInterval( 'P1M' ) ) // forward 1 month
->setDate( $datetime->format('Y'), $datetime->format('m'), 1 ) // set to 1st of that month
->setTime( 0, 0, 0, -1 ); // reset time to 00:00:00 and then subtract 1 microsecond,
// this yields 23:59:59.999999 of previous month, which is the month we want :)
This gives the 1st of the given month at the very start (00:00:00.000000), and the last of the given month at the very end (23:59:59.999999) in your timezone. (Be mindful of database's timezone being different, in which case, use DateTime::setTimezone for precision)
References: