1) $t -= 1; if ($t < 1/6) return $p + ($q - $p) * 6 * $t; if ($t < 1/2) return $q; if ($t < 2/3) return $p + ($q - $p) * (2/3 - $t) * 6; return $p; } /** * Calculate the next start time based on previous transmissions in a block * @param PDO $db Database connection * @param string $blockDate Date in Y-m-d format * @param string $channel Channel name * @param string $blockStartTime Optional block start time override * @return string Time in H:i:s format */ function calculateNextStartTime($db, $blockDate, $channel, $blockStartTime = null) { // Get all transmissions for this block, ordered by time $stmt = $db->prepare(" SELECT start_time, duration FROM transmissions WHERE start_date = ? AND channel = ? ORDER BY start_time ASC "); $stmt->execute([$blockDate, $channel]); $transmissions = $stmt->fetchAll(PDO::FETCH_ASSOC); // If no transmissions, return block start time if (empty($transmissions)) { if ($blockStartTime) { return $blockStartTime; } // Get block start time from daily_blocks or template $stmt = $db->prepare(" SELECT actual_start_time FROM daily_blocks WHERE block_date = ? AND channel = ? LIMIT 1 "); $stmt->execute([$blockDate, $channel]); $time = $stmt->fetchColumn(); return $time ?: '07:00:00'; // Default fallback } // Calculate end time of last transmission $lastTx = end($transmissions); $startTime = new DateTime($lastTx['start_time']); // Add duration to get end time list($h, $m, $s) = explode(':', $lastTx['duration']); $interval = new DateInterval("PT{$h}H{$m}M{$s}S"); $startTime->add($interval); return $startTime->format('H:i:s'); } /** * Convert time string to seconds * @param string $time Time in HH:MM:SS format * @return int Seconds */ function timeToSeconds($time) { list($h, $m, $s) = explode(':', $time); return ($h * 3600) + ($m * 60) + $s; } /** * Convert seconds to time string * @param int $seconds Seconds * @return string Time in HH:MM:SS format */ function secondsToTime($seconds) { $hours = floor($seconds / 3600); $minutes = floor(($seconds % 3600) / 60); $secs = $seconds % 60; return sprintf('%02d:%02d:%02d', $hours, $minutes, $secs); } /** * Add time duration to a time string * @param string $startTime Time in HH:MM:SS format * @param string $duration Duration in HH:MM:SS format * @return string Resulting time in HH:MM:SS format */ function addTimeToTime($startTime, $duration) { $startSeconds = timeToSeconds($startTime); $durationSeconds = timeToSeconds($duration); $totalSeconds = $startSeconds + $durationSeconds; return secondsToTime($totalSeconds); } /** * Check if two time ranges overlap * @param string $start1 Start time 1 * @param string $end1 End time 1 * @param string $start2 Start time 2 * @param string $end2 End time 2 * @return bool True if overlap exists */ function timeRangesOverlap($start1, $end1, $start2, $end2) { $s1 = timeToSeconds($start1); $e1 = timeToSeconds($end1); $s2 = timeToSeconds($start2); $e2 = timeToSeconds($end2); return ($s1 < $e2) && ($e1 > $s2); } /** * Get or create daily blocks for a specific date range * @param PDO $db Database connection * @param string $startDate Start date * @param string $endDate End date * @return array Array of daily blocks */ function ensureDailyBlocks($db, $startDate, $endDate) { $start = new DateTime($startDate); $end = new DateTime($endDate); $interval = new DateInterval('P1D'); $period = new DatePeriod($start, $interval, $end->modify('+1 day')); $dayMap = [ 'Monday' => 'ma', 'Tuesday' => 'di', 'Wednesday' => 'wo', 'Thursday' => 'do', 'Friday' => 'vr', 'Saturday' => 'za', 'Sunday' => 'zo' ]; foreach ($period as $date) { $dateStr = $date->format('Y-m-d'); $dayOfWeek = $dayMap[$date->format('l')]; // Get active templates for this day $stmt = $db->prepare(" SELECT * FROM block_templates WHERE is_active = 1 AND (day_of_week = ? OR day_of_week = 'all') "); $stmt->execute([$dayOfWeek]); $templates = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($templates as $template) { // Check if daily block already exists for this template $stmt = $db->prepare(" SELECT id FROM daily_blocks WHERE block_date = ? AND channel = ? AND template_id = ? "); $stmt->execute([ $dateStr, $template['channel'], $template['id'] ]); if (!$stmt->fetch()) { // Create daily block from template $stmt = $db->prepare(" INSERT IGNORE INTO daily_blocks (template_id, channel, block_date, actual_start_time, actual_end_time) VALUES (?, ?, ?, ?, ?) "); $stmt->execute([ $template['id'], $template['channel'], $dateStr, $template['default_start_time'], $template['default_end_time'] ]); } } } } /** * Format date for display in Dutch * @param string $date Date string * @return string Formatted date */ function formatDateDutch($date) { $dt = new DateTime($date); $dayNames = ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za']; $monthNames = [ '', 'januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december' ]; $dayOfWeek = $dayNames[$dt->format('w')]; $day = $dt->format('j'); $month = $monthNames[(int)$dt->format('n')]; $year = $dt->format('Y'); return ucfirst($dayOfWeek) . ' ' . $day . ' ' . $month . ' ' . $year; } /** * Get database connection * @return PDO Database connection */ function getDbConnection() { static $db = null; if ($db === null) { $db = new PDO( "mysql:host={$_ENV['DB_HOST']};dbname={$_ENV['DB_NAME']};charset=utf8mb4", $_ENV['DB_USER'], $_ENV['DB_PASS'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false ] ); } return $db; } /** * Send JSON response * @param mixed $data Data to send * @param int $statusCode HTTP status code */ function jsonResponse($data, $statusCode = 200) { http_response_code($statusCode); header('Content-Type: application/json'); echo json_encode($data); exit; } /** * Validate time format (HH:MM:SS or HH:MM) * @param string $time Time string * @return bool True if valid */ function isValidTime($time) { return preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/', $time); } /** * Validate date format (YYYY-MM-DD) * @param string $date Date string * @return bool True if valid */ function isValidDate($date) { $d = DateTime::createFromFormat('Y-m-d', $date); return $d && $d->format('Y-m-d') === $date; }