Currently (in 6.4.2), WordPress schedules the checks to occur every 12 hours from the first time a page load results in calling this function from wp-includes/update.php
:
function wp_schedule_update_checks() {
if ( ! wp_next_scheduled( 'wp_version_check' ) && ! wp_installing() ) {
wp_schedule_event( time(), 'twicedaily', 'wp_version_check' );
}
if ( ! wp_next_scheduled( 'wp_update_plugins' ) && ! wp_installing() ) {
wp_schedule_event( time(), 'twicedaily', 'wp_update_plugins' );
}
if ( ! wp_next_scheduled( 'wp_update_themes' ) && ! wp_installing() ) {
wp_schedule_event( time(), 'twicedaily', 'wp_update_themes' );
}
}
Subsequent occurrences get scheduled by wp-cron.php
calling wp_reschedule_event
, which adds another 12 hours (or several multiples of 12 hours if necessary) to the existing timestamp and then calls wp_schedule_event
again.
Partially inspired by your question, I am using this filter to restrict the time range during which updates may occur:
// Only allow auto-updates during Mon-Thu mornings so we'll have time during business hours to fix any problems that arise.
// See also wp-admin/includes/class-wp-automatic-updater.php
function my_auto_update( $update, $item ) {
$tz = new DateTimeZone('America/Chicago');
$now = new DateTimeImmutable('now', $tz);
if ($now < new DateTimeImmutable('07:50', $tz)) $update = false;
if ($now > new DateTimeImmutable('12:00', $tz)) $update = false;
// https://www.php.net/manual/en/datetime.format.php
// 1 (for Monday) through 7 (for Sunday)
if ($now->format('N') >= 5) $update = false;
// otherwise, use the normal API response to decide whether to update or not
return $update;
}
add_filter( 'auto_update_core', 'my_auto_update', 10, 2 );
add_filter( 'auto_update_plugin', 'my_auto_update', 10, 2 );
add_filter( 'auto_update_theme', 'my_auto_update', 10, 2 );
add_filter( 'auto_update_translation', 'my_auto_update', 10, 2 );
but have also added a second filter to adjust the scheduled times to my liking:
// Override default arbitrary twicedaily timing of wp_schedule_update_checks (in wp-includes/update.php) to choose a specific time that potentially satisfies the above filter, so we won't continually miss our window by sheer bad luck.
// Note that we still need a page load at an acceptable time to actually trigger the task; see also https://developer.wordpress.org/plugins/cron/
function my_schedule_autoupdate($event) {
switch ($event->hook) {
case 'wp_version_check':
case 'wp_update_plugins':
case 'wp_update_themes':
$desired_time = new DateTime('07:50', new DateTimeZone('America/Chicago'));
if ($desired_time->getTimestamp() < time()) $desired_time->modify('+1 day');
$event->timestamp = $desired_time->getTimestamp();
$event->schedule = "daily";
}
return $event;
}
add_filter( 'schedule_event', 'my_schedule_autoupdate' );
That still doesn't guarantee success, because WP-Cron is only triggered on page load; what if nobody browses the site during the acceptable update window? This final piece of the puzzle is solved by using system cron to run WP-Cron at a desirable time, in my case:
# min hr dom mon dow command
55 7 * * 1-4 curl --no-progress-meter --insecure https://localhost/wp-cron.php
note: a side effect of my_auto_update
is that the Plugins > Installed Plugins page shows "Auto-updates disabled" for every plugin, unless you visit it during the acceptable hours. I haven't spent any time trying to code around this; when I need to enable auto-update for new plugins, I can either remember to do it during the morning or I can temporarily disable the filter first. (Of course, I wouldn't need to manually enable auto-update for new plugins at all if my filter explicitly returned true
during the acceptable time window, but that would also make it harder to exclude a particular plugin from auto-updating.)