WordPress’s WP_Query supports a powerful date_query parameter that goes far beyond simple year/month filters. The date_query accepts an array of date conditions that can be combined with AND or OR relations, filter by specific date components (year, month, week, day, hour, minute, second), use comparison operators (between two dates, before a date, after a date), and target either the post publish date (post_date) or the modified date (post_modified) or a custom date-type column. This is used for: time-limited content (show only this week’s events), archives filtered by date range, “updated within last 30 days” queries, and editorial workflows that filter by scheduled publish dates. Understanding the column and inclusive/exclusive boundary behaviour avoids off-by-one errors in date ranges.
Problem: An events archive needs three queries: (1) events published in Q1 of the current year, (2) posts modified within the last 30 days, and (3) posts published on any Monday in the past six months. Each query must use date_query correctly with the right column, comparison, and inclusive/exclusive boundaries.
Solution: Build each query using the date_query array with the appropriate column, compare, after, before, and dayofweek arguments.
<?php
$current_year = (int) date( 'Y' );
// ── (1) Posts published in Q1 of current year ─────────────────────────
$q1_query = new WP_Query( [
'post_type' => 'event',
'date_query' => [
[
'year' => $current_year,
'month' => [ 1, 2, 3 ], // January through March
'compare' => 'IN',
],
],
] );
// ── (2) Posts modified within the last 30 days ────────────────────────
$recent_modified = new WP_Query( [
'post_type' => 'post',
'posts_per_page' => 10,
'date_query' => [
[
'column' => 'post_modified', // default is post_date
'after' => '30 days ago', // strtotime-parseable string
],
],
] );
// ── (3) Posts published on any Monday in the last 6 months ───────────
$monday_posts = new WP_Query( [
'post_type' => 'post',
'date_query' => [
'relation' => 'AND',
[
'after' => '6 months ago',
'inclusive' => true, // include the boundary date
],
[
'dayofweek' => 2, // 1=Sun, 2=Mon, ... 7=Sat
],
],
] );
// ── Date range with before/after using array format ───────────────────
$date_range = new WP_Query( [
'date_query' => [
[
'after' => [ 'year' => 2021, 'month' => 10, 'day' => 1 ],
'before' => [ 'year' => 2021, 'month' => 10, 'day' => 31 ],
'inclusive' => true,
],
],
] );
// ── OR relation: posts from January OR posts from July ────────────────
$jan_or_jul = new WP_Query( [
'date_query' => [
'relation' => 'OR',
[ 'month' => 1 ],
[ 'month' => 7 ],
],
] );
// ── dayofweek_iso: 1=Mon through 7=Sun (ISO standard) ─────────────────
$friday_posts = new WP_Query( [
'date_query' => [ [ 'dayofweek_iso' => 5 ] ], // Friday
] );
// ── Available date query keys ──────────────────────────────────────────
// year, month, week, day, hour, minute, second
// dayofweek (1=Sun..7=Sat), dayofweek_iso (1=Mon..7=Sun), dayofyear
// after, before (strtotime string or ['year','month','day'] array)
// compare: '=','!=','>','>=','<','<=','BETWEEN','NOT BETWEEN','IN','NOT IN'
NOTE: By default, date_query targets the post_date column (the publish date in the site's local timezone as stored in the database). WordPress stores both post_date (local time) and post_date_gmt (UTC) — the date_query always compares against the local time column by default. Set 'column' => 'post_date_gmt' to compare against UTC instead. The inclusive flag determines whether the boundary dates themselves are included in the result — the default is false (exclusive), meaning 'after' => '2021-10-01' returns posts from 2021-10-02 onwards. Set 'inclusive' => true to include the boundary date. The 'week' key uses MySQL's WEEK() function, which depends on the server's @@global.default_week_format setting — use dayofweek comparisons for portable week-day filtering.