I use the following code to exclude expired posts and it works fine but it doesn't exclude expired sticky posts.
//* Exclude expired posts
add_action( 'pre_get_posts', 'exclude_expired_posts' );
function exclude_expired_posts( $query ) {
if (
!is_admin()
&& $query->is_main_query()
&& !is_search()
&& !is_singular()
&& !is_date()
) {
$meta_query = array (
'relation' => 'OR',
array(
'key' =>'ed_expiry',
'compare' => 'NOT EXISTS'
),
array(
'key' => 'ed_expiry',
'value' => current_time( 'mysql' ),
'type' => 'DATETIME',
'compare' => '>',
),
);
$query->set('meta_query', $meta_query);
}
}