WP_Date_Query Generator
Build a WordPress date_query: before, after, between, a relative window or a specific year and month, on the column and timezone you mean.
<?php
/**
* Posts filtered by date.
*/
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
'date_query' => array(
array(
'column' => 'post_date',
'inclusive' => true,
// Evaluated by strtotime when the query runs, not when it was written.
'after' => '30 days ago',
),
),
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
the_title( '<h2>', '</h2>' );
}
wp_reset_postdata();
}
Output is valid and updates as you type.
Fix the highlighted fields to update the output.
Pick the window you want and the generator writes the date_query array: a range, a rolling window, a specific month, or a time of day, against the column that actually holds the date you mean.
How to use
- Choose the mode first. A fixed range and a rolling window look similar and behave completely differently once the code has been running for a month.
- Leave
inclusiveon unless you know you want it off. With it off, “after 2026-01-01” starts on the 2nd. - Pick the column deliberately.
post_dateis the site’s local time;post_date_gmtis UTC, and is what a cron job or an API comparison should use. - Use a relative string such as
30 days agofor a rolling window. It is evaluated bystrtotime()when the query runs, so the window moves with the site. - Combine with
post_typeandposts_per_page. A date query does not limit anything on its own.
Example
Posts from the last 30 days, inclusive, against the local date column:
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
'date_query' => array(
array(
'column' => 'post_date',
'inclusive' => true,
'after' => '30 days ago',
),
),
);
A literal date would freeze the window on the day it was written. The relative string is what keeps “the last 30 days” true tomorrow.
Pitfalls
inclusivedefaults to false. “After 1 January” then means “from 2 January”, which is the single most reported date query surprise.post_dateis local,post_date_gmtis UTC. Comparing a UTC timestamp against the local column is wrong by your offset, twice a year by an extra hour.- A relative string is evaluated at query time. That is usually what you want, and it makes the query uncacheable by exact key.
year,monthanddayare separate arguments, not a date.month => 1matches January in every year unless you also give a year.- The
hourargument ignores the date entirely: it matches that hour on any day, which is how “posts published in office hours” is built. beforewith a bare date means midnight at the start of that day, so the day itself is excluded unlessinclusiveis on.- Date queries cannot use the object cache, and on a large
wp_poststable an unindexed range is a slow query. - Mixing
date_querywith theyear,monthnumordaytop level arguments gives two sets of conditions. Pick one.
Compatibility
date_query has been in WP_Query since WordPress 3.7, and works the same way in WP_Comment_Query and WP_User_Query with a different default column. inclusive, column and relation have been available since the same release. The generated code targets PHP 7.0 and up, and the tool runs entirely in your browser.
Frequently asked questions
Why is my start date missing from the results?
inclusive is off. Turn it on, or move the boundary back a day.Which column should I use?
post_date for anything shown to a visitor in site time. post_date_gmt for comparisons against a UTC timestamp, including anything in a cron job.Can I query by a custom date field?
date_query. A meta field needs meta_query with type => DATE, and the value must be stored in YYYY-MM-DD for the comparison to work.How do I get posts from this month?
after => 'first day of this month', which strtotime() understands.Does it respect the site timezone?
strtotime() uses PHP’s timezone, which WordPress sets to UTC, so a relative string is compared in UTC terms.