| @@ -231,12 +231,9 @@ | ||
| 231 | 231 | if ( '' === $key ) { |
| 232 | 232 | $key = 'lasso-missing-site-key'; |
| 233 | 233 | } |
| 234 | 234 | |
| 235 | - $hash = abs( crc32( $key ) ); | |
| 236 | - // Minutes 60–1439 (hour 1–23). UTC hour 0 stays for sites that never | |
| 237 | - // upgrade so 00Z is not re-occupied by hashed slots. | |
| 238 | - $minute_of_day = 60 + ( $hash % 1380 ); | |
| 235 | + $minute_of_day = abs( crc32( $key ) ) % 1440; | |
| 239 | 236 | |
| 240 | 237 | return array( |
| 241 | 238 | 'minute_of_day' => $minute_of_day, |
| 242 | 239 | 'hour' => (int) floor( $minute_of_day / 60 ), |
| @@ -251,9 +248,9 @@ | ||
| 251 | 248 | * @param int $current_time Unix timestamp. |
| 252 | 249 | * @return int |
| 253 | 250 | */ |
| 254 | 251 | public static function next_daily_run_ts( $slot, $current_time ) { |
| 255 | - $hour = isset( $slot['hour'] ) ? max( 1, min( 23, (int) $slot['hour'] ) ) : 1; | |
| 252 | + $hour = isset( $slot['hour'] ) ? max( 0, min( 23, (int) $slot['hour'] ) ) : 0; | |
| 256 | 253 | $minute = isset( $slot['minute'] ) ? max( 0, min( 59, (int) $slot['minute'] ) ) : 0; |
| 257 | 254 | $today = gmmktime( $hour, $minute, 0 ); |
| 258 | 255 | if ( $today > $current_time ) { |
| 259 | 256 | return $today; |
| @@ -273,18 +270,14 @@ | ||
| 273 | 270 | $offset = isset( $slot['minute'] ) ? ( (int) $slot['minute'] % 15 ) : 0; |
| 274 | 271 | $cur_min = (int) gmdate( 'i', $current_time ); |
| 275 | 272 | $hour = (int) gmdate( 'G', $current_time ); |
| 276 | 273 | $window = $cur_min - ( $cur_min % 15 ); |
| 277 | - $candidate = gmmktime( $hour, $window + $offset, 0 ); | |
| 278 | - if ( $candidate <= $current_time ) { | |
| 279 | - $candidate += 15 * MINUTE_IN_SECONDS; | |
| 274 | + $candidate = gmmktime( $hour, $window + $offset, 0 ); | |
| 275 | + if ( $candidate > $current_time ) { | |
| 276 | + return $candidate; | |
| 280 | 277 | } |
| 281 | - // Do not land 15-minute events in UTC hour 0. | |
| 282 | - while ( 0 === (int) gmdate( 'G', $candidate ) ) { | |
| 283 | - $candidate += 15 * MINUTE_IN_SECONDS; | |
| 284 | - } | |
| 285 | 278 | |
| 286 | - return $candidate; | |
| 279 | + return $candidate + ( 15 * MINUTE_IN_SECONDS ); | |
| 287 | 280 | } |
| 288 | 281 | |
| 289 | 282 | /** |
| 290 | 283 | * Whether a scheduled timestamp sits on the daily slot. |
| @@ -293,12 +286,8 @@ | ||
| 293 | 286 | * @param array $slot site_load_slot(). |
| 294 | 287 | * @return bool |
| 295 | 288 | */ |
| 296 | 289 | public static function timestamp_matches_daily_slot( $timestamp, $slot ) { |
| 297 | - if ( 0 === (int) gmdate( 'G', $timestamp ) ) { | |
| 298 | - return false; | |
| 299 | - } | |
| 300 | - | |
| 301 | 290 | return (int) gmdate( 'G', $timestamp ) === (int) $slot['hour'] |
| 302 | 291 | && (int) gmdate( 'i', $timestamp ) === (int) $slot['minute']; |
| 303 | 292 | } |
| 304 | 293 | |
| @@ -309,12 +298,8 @@ | ||
| 309 | 298 | * @param array $slot site_load_slot(). |
| 310 | 299 | * @return bool |
| 311 | 300 | */ |
| 312 | 301 | public static function timestamp_matches_15m_slot( $timestamp, $slot ) { |
| 313 | - if ( 0 === (int) gmdate( 'G', $timestamp ) ) { | |
| 314 | - return false; | |
| 315 | - } | |
| 316 | - | |
| 317 | 302 | $offset = isset( $slot['minute'] ) ? ( (int) $slot['minute'] % 15 ) : 0; |
| 318 | 303 | return ( (int) gmdate( 'i', $timestamp ) % 15 ) === $offset; |
| 319 | 304 | } |
| 320 | 305 | |
| @@ -395,14 +380,10 @@ | ||
| 395 | 380 | return $age > 60; |
| 396 | 381 | } |
| 397 | 382 | |
| 398 | 383 | /** |
| 399 | - * Hourly due window: current hour plus late catch-up (2h, or 3h in UTC hour 1). | |
| 384 | + * Hourly due window: current hour plus up to 2h of late catch-up. | |
| 400 | 385 | * |
| 401 | - * 15-minute crons skip UTC hour 0, so the first tick after midnight is ~01:xx. | |
| 402 | - * A 2h lookback from 01:xx starts ~23:xx and drops the 22:xx band that a 00:xx | |
| 403 | - * tick used to catch (WP-Cron often misses the last evening window). | |
| 404 | - * | |
| 405 | 386 | * @param string $item_key Request URL. |
| 406 | 387 | * @param int|null $now Unix timestamp. |
| 407 | 388 | * @param int|null $last_tick Previous tick (0 = first run). |
| 408 | 389 | * @param int|null $due_minute Injected slot for tests. |
| @@ -416,10 +397,9 @@ | ||
| 416 | 397 | if ( $last_tick <= 0 ) { |
| 417 | 398 | $last_tick = $now - HOUR_IN_SECONDS; |
| 418 | 399 | } |
| 419 | 400 | |
| 420 | - $lookback = ( 1 === (int) gmdate( 'G', $now ) ) ? ( 3 * HOUR_IN_SECONDS ) : ( 2 * HOUR_IN_SECONDS ); | |
| 421 | - $window_start = max( $last_tick - 300, $now - $lookback ); | |
| 401 | + $window_start = max( $last_tick - 300, $now - ( 2 * HOUR_IN_SECONDS ) ); | |
| 422 | 402 | if ( null === $due_minute ) { |
| 423 | 403 | $due_minute = self::item_due_minute( self::site_schedule_key(), $item_key ); |
| 424 | 404 | } |
| 425 | 405 | |
| @@ -428,10 +408,10 @@ | ||
| 428 | 408 | if ( $due_today <= $now ) { |
| 429 | 409 | return $due_today > $window_start; |
| 430 | 410 | } |
| 431 | 411 | |
| 432 | - // Due later today: still catch yesterday's occurrence inside the lookback | |
| 433 | - // window (WP-Cron often fires after UTC midnight and would otherwise skip 22:00–23:59). | |
| 412 | + // Due later today: still catch yesterday's occurrence inside the 2h window | |
| 413 | + // (WP-Cron often fires after UTC midnight and would otherwise skip 22:00–23:59). | |
| 434 | 414 | return ( $due_today - DAY_IN_SECONDS ) > $window_start; |
| 435 | 415 | } |
| 436 | 416 | |
| 437 | 417 | /** |