Okay, I think this is fixed.
I came up with a script to delete the duplicate postmeta data. It is pretty brainless, but I’m going to operate on my theory that some botched import jobs caused this and hope that this doesn’t happen again.
I also found that the problem with the full calendar view was that for some reason the WP_Query was being altered by the time the loop for the monthly display started. I don’t know why this didn’t crop up before, and I don’t know exactly where the conflict is, but I implemented this silly hack before the loop that seems to work:
global $wp_query;
$wp_query = new WP_Query($wp_query->query);
have_posts()) : ?>
have_posts() ) : $wp_query->the_post(); ?>
etc.