Order By Post Meta DATE in WP_Query

Sorting by post meta in WP_Query is quite easy with the orderby argument set to meta_value. If your meta value is a number, you can use meta_value_num, however, if you run into a situation where you have a date string, like 2012-06-15, both meta_value and meta_value_num won’t give you the desired results.

Luckily, you can hook into the posts_orderby filter and do some custom SQL, like this:

add_filter( 'posts_orderby', 'my_posts_orderby_date', 10, 2 );
function my_posts_orderby_date( $orderby, $query ) {
    global $wpdb;
    return " CAST( $wpdb->postmeta.meta_value AS DATE ) " . $query->get( 'order' );
}

Then do your regular WP_Query with both orderby and meta_key arguments, and no matter to what orderby is set to, it’ll use the SQL generated in the filter.

However, keep in mind that CASTs aren’t the fastest and most efficient queries in MySQL, so think about using timestamp (integers) for meta values and using meta_value_num for sorting. You have PHP functions such as strtotime that can help you format a date and time string, into a numeric timestamp.

Cheers, and stay updated!

About the author

Konstantin Kovshenin

WordPress Core Contributor, ex-Automattician, public speaker and consultant, enjoying life in Moscow. I blog about tech, WordPress and DevOps.

2 comments

  • where you have a date string, like 2012-06-15, both meta_value and meta_value_num won’t give you the desired results.

    I have a working example where meta_value sorts by date. Dates are in exactly same form.

    It was just yesterday where I had different issue with those fields: I tried to get posts from date range with meta_query, ‘BETWEEN’, and ‘DATE’ but it didn’t work. Eventually I created two queries with ‘>=’ and ‘<=', merging of arrays and finally third query with post__in. So painful.

    Then do your regular WP_Query or get_posts

    Actually, you don’t with get_posts since it sets suppress_filters to true. ;)

    And last thing, I would always mention to remove filter after the query since otherwise you can get another mysterious not working query.

    • Hi Milan, thanks for the suppress_filters heads up! It looks like your meta_value sort worked because MySQL will sort using CHAR by default,

      Note also that the sorting will be alphabetical which is fine for strings (i.e. words), but can be unexpected for numbers (e.g. 1, 3, 34, 4, 56, 6, etc, rather than 1, 3, 4, 6, 34, 56 as you might naturally expect).

      Which in your (and my) case might work, but can easily fail over something that is very simple, like 6 instead of 06 for June :)

      Thanks for your comment!