all( 1000 )as$location ) { if ( ( $location['status'] ?? '' ) !== 'active' ) { continue; } $id = (int) $location['id']; $raw = apply_filters( 'abientot_location_weekly_schedule', get_option( 'abientot_location_weekly_schedule', null ), $location, array() ); if ( ! is_array( $raw ) ) { continue; } try { $zone = new \DateTimeZone( $location['timezone'] ?: Settings::values()['business_timezone'] ); $end = ( new \DateTimeImmutable( '@' . $now ) )->setTimezone( $zone )->setTime( 0, 0 )->modify( '+2 days' ); $first = $end->modify( '-' . max( 370, (int) ceil( $hours / 24 ) + 14 ) . ' days' ); $windows = array(); $blocks = array(); for ( $anchor = $first;$anchor <= $end;$anchor = $anchor->modify( '+1 day' ) ) { $windows = array_merge( $windows, ScheduleIntervals::intervals( $raw[ (int) $anchor->format( 'w' ) ] ?? array(), $anchor ) ); } // Legacy location-wide full-day closures were saved against the default staff. $default_staff = ( new StaffRepository() )->default_staff(); $exceptions = $wpdb->get_results( $wpdb->prepare( "SELECT exception_date,exception_type,start_time,end_time FROM " . $wpdb->prefix . "abientot_availability_exceptions WHERE location_id=%d AND (staff_id IS NULL OR (staff_id=%d AND exception_type='closed' AND start_time IS NULL AND end_time IS NULL)) AND service_id IS NULL AND exception_date BETWEEN %s AND %s ORDER BY exception_date,id", $id, (int) ( $default_staff['id'] ?? 0 ), $first->format( 'Y-m-d' ), $end->format( 'Y-m-d' ) ), ARRAY_A ); foreach ( $exceptions ?: array()as$row ) { $anchor = new \DateTimeImmutable( $row['exception_date'], $zone ); if ( $row['exception_type'] === 'closed' && $row['start_time'] === null ) { $blocks[] = array( 'start' => $anchor, 'end' => $anchor->modify( '+1 day' ) ); continue; } if ( ! $row['start_time'] || ! $row['end_time'] ) { continue; } $parts = ScheduleIntervals::intervals( array( 'periods' => array( array( 'start_time' => $row['start_time'], 'end_time' => $row['end_time'] ) ) ), $anchor ); if ( $row['exception_type'] === 'open' ) { $windows = array_merge( $windows, $parts ); } elseif ( in_array( $row['exception_type'], array( 'closed', 'break' ), true ) ) { $blocks = array_merge( $blocks, $parts ); } } $windows = ScheduleIntervals::merge( $windows ); foreach ( $blocks as$block ) { $windows = ScheduleIntervals::subtract( $windows, $block ); } $intervals = array_map( static fn( array $w ): array=>array( $w['start']->getTimestamp(), $w['end']->getTimestamp() ), $windows ); $has_previous = false; foreach ( $intervals as$interval ) { if ( $interval[1] <= $now ) { $has_previous = true; break; } } $out[ $id ] = $has_previous ? NotificationWindow::since( $now, $hours, $intervals, true ) : 0; } catch ( \Throwable $error ) { $out[ $id ] = $base; } } return $out; } }