I agree with @Tom J Nowell:
I’d suggest you remove it, or better yet just say “all rights reserved”.
But in the quest for answers, here’s a suggestion using an option field to cache the years:
/**
* Get year range for posts.
*
* @return str
*/
function wpse_226627_get_copyright() {
if ( ! $years = get_option( 'copyright' ) ) {
$args = [
'posts_per_page' => 1,
'post_type' => get_post_types([ 'public' => true ]),
'post_status' => 'publish',
'orderby' => 'post_date',
/**
* Don't waste memory we don't need
*/
'update_post_term_cache' => false,
'update_post_meta_cache' => false,
'cache_results' => false,
];
$newest = get_posts([ 'order' => 'DESC' ] + $args );
$oldest = get_posts([ 'order' => 'ASC' ] + $args );
$years = [
'from' => $oldest ? mysql2date( 'Y', $oldest[0]->post_date_gmt ) : '',
'to' => $newest ? mysql2date( 'Y', $newest[0]->post_date_gmt ) : '',
];
update_option( 'copyright_years', $years );
}
return $years;
}
/**
* Bust the cache.
*/
function wpse_226627_flush_years( $post_id ) {
if ( in_array( get_post_type( $post_id ), get_post_types([ 'public' => true ]) ) )
update_option( 'copyright_years', '' );
}
add_action( 'before_delete_post', 'wpse_226627_flush_years' );
add_action( 'save_post', 'wpse_226627_flush_years' );
This way the cache is flushed & populated only when a post is created/updated/deleted, so a couple of extra queries (instead of one direct database call) is no real biggy here.
$years = wpse_226627_get_years();
$copyright = $years['from'];
if ( $years['from'] != $years['to'] )
$copyright .= ' — ' . $years['to'];
I chose to cache the years as a serialized array (as opposed to the compiled copyright string) so that you’ve always got the values to work with independently, should you ever wish to change their layout/do something differently with them.