Change a Post’s Status based on the date from a custom field? (for Event posts)

There are several little issues with your code. This is what I use for the exact same thing though and it works:

// expire events on date field.
if (!wp_next_scheduled('expire_events')){
  wp_schedule_event(time(), 'daily', 'expire_events'); // this can be hourly, twicedaily, or daily
}

add_action('expire_events', 'expire_events_function');

function expire_events_function() {
    $today = date('Ymd');
    $args = array(
        'post_type' => array('event'), // post types you want to check
        'posts_per_page' => -1 
    );
    $posts = get_posts($args);
    foreach($posts as $p){
        $expiredate = get_field('event_and_date', $p->ID, false, false); // get the raw date from the db
        if ($expiredate) {
            if($expiredate < $today){
                $postdata = array(
                    'ID' => $p->ID,
                    'post_status' => 'draft'
                );
                wp_update_post($postdata);
            }
        }
    }
}

Place this code in your functions.php and load the site one time to set your CRON job. You can text to ensure your CRON is set and run it right away as well with wp-cli or a CRON plugin.

I would suggest next time though to prefix your post types so you know they are your own, for cleaner code, and to reduce potential conflicts. (ie event could be nom_event)

EDIT: Try changing this line:

$expiredate = get_field('event_and_date', $p->ID, false, false);

To this:

$expiredate = get_post_meta($p->ID, 'event_end_date', true );

(this line is what your original code should have looked like as well…without the $ in front of event_end_date)

If this isn’t working you can contact me on my site (it’s pretty easy to figure out)