1const TIME_ZONE = "Asia/Jerusalem"; 2const RELEASE_HOUR = 18; 3const RELEASE_MINUTE = 45; 4 5const PROGRAM_ORDER = [ 6 "hamoach", 7 "songs-quiz", 8 "film-ghosts", 9 "marketplace-price", 10 "daily-news", 11 "music-battle", 12]; 13 14function israelParts(date) { 15 return Object.fromEntries( 16 new Intl.DateTimeFormat("en-CA", { 17 timeZone: TIME_ZONE, 18 year: "numeric", 19 month: "2-digit", 20 day: "2-digit", 21 hour: "2-digit", 22 minute: "2-digit", 23 hourCycle: "h23", 24 }).formatToParts(date).map((part) => [part.type, part.value]), 25 ); 26} 27 28function israelDateKey(date) { 29 const parts = israelParts(date); 30 return `${parts.year}-${parts.month}-${parts.day}`; 31} 32 33export function scheduleTimeReleaseReached(now = new Date()) { 34 const parts = israelParts(now); 35 const hour = Number(parts.hour); 36 const minute = Number(parts.minute); 37 return hour > RELEASE_HOUR || 38 (hour === RELEASE_HOUR && minute >= RELEASE_MINUTE); 39} 40 41export function authoritativeScheduleTimes(schedule, now = new Date()) { 42 if (!scheduleTimeReleaseReached(now) || !Array.isArray(schedule)) { 43 return null; 44 } 45 46 const today = israelDateKey(now); 47 const byProgram = new Map(); 48 for (const slot of schedule) { 49 if (!PROGRAM_ORDER.includes(slot?.programKey)) continue; 50 const startsAt = new Date(slot?.startsAt ?? ""); 51 const endsAt = new Date(slot?.endsAt ?? ""); 52 if ( 53 !Number.isFinite(startsAt.getTime()) || 54 !Number.isFinite(endsAt.getTime()) || 55 endsAt <= startsAt 56 ) { 57 return null; 58 } 59 if (israelDateKey(startsAt) !== today) continue; 60 if ( 61 slot?.isPersisted !== true || 62 !String(slot?.showPackageId ?? "").trim() || 63 byProgram.has(slot.programKey) 64 ) { 65 return null; 66 } 67 byProgram.set(slot.programKey, { ...slot, startsAt, endsAt }); 68 } 69 70 const order = PROGRAM_ORDER.filter(key => key !== "film-ghosts" || byProgram.has(key)); 71 if (byProgram.size !== order.length) return null; 72 const ordered = order.map((programKey) => byProgram.get(programKey)); 73 if (ordered.some((slot, index) => 74 !slot || (index > 0 && slot.startsAt <= ordered[index - 1].startsAt) 75 )) { 76 return null; 77 } 78 // Once the full lineup has ended, this dialog represents the next 79 // broadcast. Do not keep presenting the completed day's exact times as if 80 // they were tomorrow's still-unproduced schedule. 81 if (now >= ordered[ordered.length - 1].endsAt) return null; 82 return ordered; 83}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.