For wordpress posts by date range, pass a date_query to WP_Query or pre_get_posts. The after / before keys take any PHP-strtotime-parseable value — ISO dates, human dates, relative offsets like '-7 days' — and inclusive => true makes the bounds themselves match. This guide covers the secondary-loop form, the pre_get_posts variant for archives, filtering by year/month parts, and the column key for “recently updated” searches.
Last verified: 2026-04-23 on WordPress 6.5 and PHP 8.3. Originally published 2022-07-17, rewritten and updated 2026-04-23.
TL;DR
$args = array(
'post_type' => 'booking',
'date_query' => array(
array(
'after' => '2026-01-01',
'before' => '2026-03-31',
'inclusive' => true,
),
),
);
$query = new WP_Query( $args );
Secondary loop — new WP_Query
$args = array(
'post_type' => 'booking',
'posts_per_page' => 20,
'date_query' => array(
array(
'after' => 'December 1st, 2025',
'before' => 'January 31st, 2026',
'inclusive' => true,
),
),
);
$bookings = new WP_Query( $args );
while ( $bookings->have_posts() ) {
$bookings->the_post();
the_title( '<h3>', '</h3>' );
}
wp_reset_postdata();
Both after and before accept any date format PHP’s strtotime() understands — ISO ('2026-01-01'), human ('January 1st, 2026'), relative ('-7 days', '2 weeks ago', 'next Monday'). inclusive => true makes both endpoints match (a post published exactly at 2026-01-01 00:00:00 is included); the default excludes them.
Main query — pre_get_posts
add_action( 'pre_get_posts', 'how7o_filter_bookings_by_date' );
function how7o_filter_bookings_by_date( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_post_type_archive( 'booking' ) ) {
$after = sanitize_text_field( $_GET['after'] ?? '' );
$before = sanitize_text_field( $_GET['before'] ?? '' );
if ( $after || $before ) {
$query->set( 'date_query', array(
array(
'after' => $after ?: null,
'before' => $before ?: null,
'inclusive' => true,
),
) );
}
}
}
This pattern lets URL parameters drive the filter — /bookings/?after=2026-01-01&before=2026-03-31 limits the archive to that range. The mandatory guards (is_admin, is_main_query, is_post_type_archive) scope the change to the frontend booking archive only. See pre_get_posts on custom post types for the full guard-discipline walkthrough.
For user-supplied dates, sanitize or validate before handing the values to date_query. strtotime() silently returns false on garbage input, so the wrong date format turns into “no filter” rather than an error — validate with DateTime::createFromFormat('Y-m-d', $input) and reject if it doesn’t parse cleanly.

Filter by year / month parts
$args = array(
'date_query' => array(
array(
'year' => 2026,
'month' => 3,
),
),
);
For natural calendar periods — everything from March 2026, every post published in Q1 2026 — use the named parts instead of after/before. Available keys: year, month, week, day, dayofweek, dayofyear, hour, minute, second. Each accepts a single value, or an array with compare: ['year' => 2025, 'month' => ['compare' => 'IN', '1, 2, 3']] for Q1 2025.
OR across multiple ranges
$args = array(
'date_query' => array(
'relation' => 'OR',
array(
'after' => '2026-01-01',
'before' => '2026-01-31',
'inclusive' => true,
),
array(
'after' => '2026-12-01',
'before' => '2026-12-31',
'inclusive' => true,
),
),
);
The relation key changes how WordPress combines the inner clauses. Default is AND (post must match every clause); OR loosens it to “match any clause.” Useful for seasonal bookings (January and December) or multi-phase promotion windows.
Filter on post_modified instead of post_date
$args = array(
'date_query' => array(
array(
'after' => '-7 days',
'column' => 'post_modified',
),
),
);
Returns posts edited (not just published) in the last week. The column key is the filter’s knob for which date column drives the comparison. Default is post_date_gmt; switch to post_modified, post_modified_gmt, or post_date to answer different questions. GMT vs local matters when the site spans timezones — use the _gmt variants for consistency in cross-timezone reports.
Frequently asked questions
'date_query' => [['after' => '2026-01-01', 'before' => '2026-03-31', 'inclusive' => true]]. Pass any PHP-strtotime-parseable date format — '2026-01-01', 'January 1st, 2026', '-7 days', '2 weeks ago'. inclusive => true makes the bounds themselves match (closed interval); default is exclusive (open interval).
$_GET directly? Validate first. WordPress runs the string through strtotime() under the hood — garbage input returns false there and the date_query effectively no-ops, but that silent fallback can mask bugs. Sanitize with a strict format parser: DateTime::createFromFormat('Y-m-d', $input) — if that returns a valid object, use it; otherwise reject. Don’t trust strtotime to do input validation.
Pass 'year', 'month', 'day', 'week', 'hour', 'minute', 'second' as keys in the same date_query clause: ['year' => 2026, 'month' => 3] gets every post from March 2026. Use this when the filter is a natural calendar period (a month’s posts, this year’s posts) rather than an arbitrary start/end range.
Yes, with the relation key: ['relation' => 'OR', ['after' => 'Jan 2026', 'before' => 'Feb 2026'], ['after' => 'Dec 2026', 'before' => 'Jan 2027']] — posts published in January 2026 OR in December 2026. Default relation is AND, which narrows the result to the intersection of every clause.
date_query work against post_modified instead of post_date? Yes — pass a column key: ['after' => '-7 days', 'column' => 'post_modified'] returns posts edited in the last week regardless of when they were originally published. Useful for a “recently updated” widget. Default column is post_date_gmt — swap to post_modified or post_modified_gmt depending on whether you’re comparing site-local or UTC.
Related guides
- How to Apply pre_get_posts on Custom Post Types in WordPress — the mandatory guards behind the main-query form.
- How to Order Posts by Meta Value in WordPress — the sibling sort pattern, often combined with date filtering.
- How to Get the Current Category ID in WordPress — combining date and category filters.
- How to Prepare a %LIKE% SQL Statement in WordPress — when
date_queryisn’t enough and you drop to$wpdb.
References
WordPress developer reference for WP_Date_Query: developer.wordpress.org/reference/classes/wp_date_query. PHP strtotime accepted formats: php.net/manual/en/datetime.formats.