Primary navigation menu with timed items from custom post type

Yes, I think it should be possible to add and remove nav menu items programmatically. You can use either wp_nav_menu_objects or wp_nav_menu_items filter to edit a nav menu. Or if you can edit the menu walker, you could add the event items inside it.

With the first filter you could use wp_update_nav_menu_item to create and save new menu items to database. You should be able to remove menu items with wp_delete_post. With the latter you will be dealing with the html of the nav menu so you can just append string of html to the menu.

If the event dates are stored as post meta, you can use custom field WP_Query to get events with future dates.

Here’s a very rough example for using wp_nav_menu_objects that I conjured up based on the Codex and the code reference. Hopefully you can use this as a starting point for creating a working solution. This is by no means a working code example and needs tweaking, fine-tuning and maybe some typo fixing.

add_filter( 'wp_nav_menu_objects', 'prefix_wp_nav_menu_objects', 10, 2 );
function prefix_wp_nav_menu_objects( $menu_items, $args ) {
  // Check if correct menu  
  if ( 'my-theme-location' === $args['theme_location'] ) {
    // Loop items
    $time_now = time();
    foreach ( $menu_items as $key => $menu_item ) {
      // Add new items to correct submenu
      if ( $add_item_logic === $menu_item->property_to_compare ) { // add applicable logic
        $new_items = prefix_add_items_menu( $menu_item, $args );
        $menu_items = $menu_items + $new_items; // add new items to the menu item array
      } else if ( $remove_item_logic === $menu_item->property_to_compare ) { // add applicable logic
        $event_date = get_post_meta( $menu_item->db_id, 'event_date', true );
        if ( $event_date && strtotime( $event_date ) < $time_now ) {
          // Delete menu item permanently
          wp_delete_post( $menu_item->db_id ); // check if property is correct
          // Remove from menu item array
          unset( $menu_items[$key] );
        }
      }      
    }

  }

  return $menu_items;
}

function prefix_add_items_menu( $menu_item, $menu_args ) {
  $new_items = array();

  $query_args = array(
    'post_type'      => 'my_cpt_slug', // add correct cpt slug
    'posts_per_page' => 10, // adjust as needed
    'meta_key'       => 'event_date', // add correct meta key if date saved in post meta
    'meta_value'     => date( "Ymd" ), // change to how "event date" is stored
    'meta_compare'   => '>=' // change comparison methof if needed
  );
  $query = new WP_Query( $query_args );

  if ( $query->posts ) {

    foreach ( $query->posts as $event ) {

      // If you want to create menu items          
      wp_update_nav_menu_item( $menu_args['menu_id'], 0, array(
        'menu-item-title'     => $event->post_title,
        'menu-item-object-id' => $event->ID,
        'menu-item-object'    => $event->post_type,
        'menu-item-parent-id' => $menu_item->ID, // not sure if property is correct, please check it yourself
      ));

      // And / or push posts to menu items
      $new_items[] = array(
        'menu-item-title'     => $event->post_title,
        'menu-item-object-id' => $event->ID,
        'menu-item-object'    => $event->post_type,
        'menu-item-parent-id' => $menu_item->ID, // not sure if property is correct, please check it yourself
      );

    }    
  }

  return $new_items;
}

You can also check the related WPSE Q&A’s on the matter,

Add items to a menu dynamically

Programmatically add a Navigation menu and menu items

Add items to a menu dynamically

Remove a menu item in menu

EDIT 1
Actually, you could perhaps update the menu on save_post, too, when a new event is published. This might be better way to update the menu now as I think about it more. Doing the update when a new post is published would mean that the update function is run less frequently thus taxing the site less overall.

For removing past events from the menu you could consider adding a custom wp cront event that would run once a day.

function prefix_update_menu( $post_id, $post ) {
  // New post only
  if ( $post->post_date !== $post->post_modified ) {
    return;
  }
  // Get main menu items
  $main_menu_items = wp_get_nav_menu_items( 'menu-id-slug-name-or-object' );
  if ( ! $main_menu_items ) {
    return;
  }

  // 1. foreach Loop menu items
  // 2. when correct submenu is matched add new menu item with wp_update_nav_menu_item
  // 3. optionally also remove passed events while doing the foreach loop
  // get post id from menu item with get_post_meta( $item->ID, '_menu_item_object_id', true );
  // then get event end date and current date for comparison
  // remove menu item with delete_postw p_delete_post( $item->ID );

}
add_action( 'save_post', 'prefix_update_menu', 10, 2 );