Pico y Placa Pereira

`; } }); } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', initAll); else initAll(); // Elementor sometimes mounts later window.addEventListener('elementor/frontend/init', () => setTimeout(initAll, 60)); let tries = 0; const t = setInterval(() => { tries++; initAll(); if (tries >= 10) clearInterval(t); }, 300); function getTZ(root){ return (root.getAttribute('data-tz') || 'America/Bogota').trim() || 'America/Bogota'; } function nowPartsInTZ(tz){ const dtf = new Intl.DateTimeFormat('en-CA', { timeZone: tz, year:'numeric', month:'2-digit', day:'2-digit', hour:'2-digit', minute:'2-digit', second:'2-digit', hour12:false }); const parts = dtf.formatToParts(new Date()); const m = {}; for (const p of parts) if (p.type !== 'literal') m[p.type] = p.value; return { year:+m.year, month:+m.month, day:+m.day, hour:+m.hour, minute:+m.minute, second:+m.second }; } function ymdFromParts(p){ return `${p.year}-${pad2(p.month)}-${pad2(p.day)}`; } function minutesOfDayFromParts(p){ return p.hour*60 + p.minute; } function dateUTCNoonFromYMD(ymd){ const [y,m,d] = ymd.split('-').map(Number); return new Date(Date.UTC(y, m-1, d, 12, 0, 0)); } function addDaysYMD(ymd, delta){ const dt = dateUTCNoonFromYMD(ymd); dt.setUTCDate(dt.getUTCDate() + delta); return `${dt.getUTCFullYear()}-${pad2(dt.getUTCMonth()+1)}-${pad2(dt.getUTCDate())}`; } function fmtTimeNoSeconds(tz){ return new Intl.DateTimeFormat('es-CO', { timeZone: tz, hour:'numeric', minute:'2-digit', hour12:true }).format(new Date()); } function weekdayUpperES(tz, ymd){ const dt = dateUTCNoonFromYMD(ymd); const w = new Intl.DateTimeFormat('es-CO', { timeZone: tz, weekday:'long' }).format(dt); return String(w).toUpperCase(); } function prettyDateES(ymd){ const [y,m,d] = ymd.split('-').map(Number); const meses = ['Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic']; return `${d} de ${meses[m-1]} ${y}`; } function isHHMM(s){ return /^([01]\d|2[0-3]):([0-5]\d)$/.test(String(s||'').trim()); } function toMinutes(hhmm){ if (!isHHMM(hhmm)) return null; const [h,m] = hhmm.split(':').map(Number); return h*60 + m; } function time24to12ES(hhmm){ if (!isHHMM(hhmm)) return String(hhmm||''); const [h,m] = hhmm.split(':').map(Number); const h12 = ((h + 11) % 12) + 1; const suf = h >= 12 ? 'p. m.' : 'a. m.'; return `${h12}:${pad2(m)} ${suf}`; } function rangeLabel(slot){ const s = String(slot.start), e = String(slot.end); if (!isHHMM(s) || !isHHMM(e)) return ''; const sMin = toMinutes(s), eMin = toMinutes(e); const crosses = (sMin !== null && eMin !== null && eMin < sMin); return crosses ? `${time24to12ES(s)} – ${time24to12ES(e)} (día sig.)` : `${time24to12ES(s)} – ${time24to12ES(e)}`; } function scheduleHtml(schedule){ const slots = Array.isArray(schedule) ? schedule : []; if (slots.length === 1 && String(slots[0].start)==="00:00" && String(slots[0].end)==="23:59") return `
Todo el día
`; if (!slots.length) return `
Sin restricción
`; if (slots.length === 1) return `
${rangeLabel(slots[0])}
`; return `
    ${slots.map(s => `
  • ${rangeLabel(s)}
  • `).join("")}
`; } // Detecta si está activo ahora y soporta cruce de medianoche function getHorarioEstado(schedule, nowMin){ const raw = (Array.isArray(schedule) ? schedule : []) .map(s => ({ s: toMinutes(String(s.start||'')), e: toMinutes(String(s.end||'')) })) .filter(x => x.s !== null && x.e !== null); if (!raw.length) return { kind:'none', endMin:null }; const slots = []; for (const slot of raw){ if (slot.e >= slot.s) slots.push(slot); else { slots.push({ s: slot.s, e: 1440 }); slots.push({ s: 0, e: slot.e, _carry:true }); } } slots.sort((a,b) => a.s - b.s); for (const slot of slots){ if (nowMin >= slot.s && nowMin < slot.e) return { kind:'active', endMin: slot.e, carry: !!slot._carry }; } return { kind:'inactive', endMin:null }; } function countdownHM(endMin, nowMin){ if (endMin === null) return null; const rem = Math.max(0, endMin - nowMin); return { h: Math.floor(rem/60), m: rem % 60 }; } function safeUrl(u){ const s = String(u ?? '').trim(); return /^https?:\/\//i.test(s) ? s : ''; } // Emojis (como pediste) function iconEmoji(key){ const k = String(key||'').toLowerCase(); if (k.includes('taxi')) return '🚕'; if (k.includes('moto')) return '🛵'; return '🚗'; } function resolveRestriction(cat, ymd){ const rule = cat.rule || {}; if (rule.type !== "month_map") return { value:"SIN INFORMACIÓN", kind:"unknown", labelMode: rule.labelMode || "terminadas" }; const ym = ymd.slice(0,7); const bucket = rule.months && rule.months[ym] ? rule.months[ym] : null; if (!bucket) return { value:"SIN INFORMACIÓN", kind:"unknown", labelMode: rule.labelMode || "terminadas" }; const noA = Array.isArray(bucket.no_aplica) ? bucket.no_aplica : []; if (noA.includes(ymd)) return { value:"NO APLICA", kind:"na", labelMode: rule.labelMode || "terminadas" }; const v = bucket.restricted && bucket.restricted[ymd] ? String(bucket.restricted[ymd]) : ""; if (!v) return { value:"SIN INFORMACIÓN", kind:"unknown", labelMode: rule.labelMode || "terminadas" }; return { value: v, kind:"digits", labelMode: rule.labelMode || "terminadas" }; } function mount(root){ const tz = getTZ(root); const key = String(root.getAttribute("data-key") || "").trim(); const cfg = window.PZ_PYP_DATA && window.PZ_PYP_DATA[key] ? window.PZ_PYP_DATA[key] : null; if (!cfg) throw new Error(`No existe window.PZ_PYP_DATA["${key}"]. Revisa data-key.`); const city = cfg.city; const sw = city.switch || {}; const baseUrl = String(sw.baseUrl || "").trim(); const options = Array.isArray(sw.options) ? sw.options : []; root.innerHTML = `
PICO Y PLACA
Hora: --:--
--
--
Si vas a viajar, revisa el pico y placa para la fecha de tu viaje.
`; const $ = (sel) => root.querySelector(sel); const elTitle = $('.pz-title'); const elTime = $('.pz-time'); const elCity = $('.pz-cityselect'); const elWeek = $('.pz-weekday'); const elDate = $('.pz-date'); const elDateInput = $('.pz-dateinput'); const elDateCenter = $('.pz-datecenter'); const elPrevDay = $('.pz-daybtn--left'); const elNextDay = $('.pz-daybtn--right'); const elGrid = $('.pz-grid'); const elFoot = $('.pz-foot'); // Select cities elCity.innerHTML = options .slice() .sort((a,b) => String(a.name||'').localeCompare(String(b.name||''), 'es')) .map(o => ``) .join(''); elCity.value = city.id; let todayTZ = ymdFromParts(nowPartsInTZ(tz)); let selectedYMD = todayTZ; function updateHeader(){ elTitle.textContent = `PICO Y PLACA ${String(city.name || city.id).toUpperCase()}`.trim(); elTime.textContent = `Hora: ${fmtTimeNoSeconds(tz)}`; elWeek.textContent = weekdayUpperES(tz, selectedYMD); elDate.textContent = prettyDateES(selectedYMD); elDateInput.value = selectedYMD; } function updateFooter(){ const srcName = String(city.source?.name || '').trim(); const srcUrl = safeUrl(city.source?.url || ''); elFoot.innerHTML = srcUrl ? `Fuente: ${srcName || 'Fuente oficial'}` : (srcName ? `Fuente: ${srcName}` : ''); } let visibleNodes = new Map(); function render(){ const cats = (city.categories || []).slice().sort((a,b) => (a.priority||99)-(b.priority||99)); elGrid.innerHTML = cats.map(cat => `
${String(cat.name || cat.id).toUpperCase()}
Placas terminadas en:
--
Horario hoy:
--
`).join(''); visibleNodes = new Map(); elGrid.querySelectorAll('.pz-card').forEach(card => { const catId = card.getAttribute('data-cat'); visibleNodes.set(catId, { label: card.querySelector('.pz-label'), value: card.querySelector('.pz-value'), cd: card.querySelector('.pz-countdown'), cdText: card.querySelector('.pz-counttext'), pill: card.querySelector('.pz-horariopill'), pillHours: card.querySelector('.pz-horariopill-hours') }); }); updateDynamic(); } function updateDynamic(){ const nowP = nowPartsInTZ(tz); todayTZ = ymdFromParts(nowP); const nowMin = minutesOfDayFromParts(nowP); const isToday = selectedYMD === todayTZ; elTime.textContent = `Hora: ${fmtTimeNoSeconds(tz)}`; const cats = (city.categories || []).slice().sort((a,b) => (a.priority||99)-(b.priority||99)); for (const cat of cats){ const refs = visibleNodes.get(String(cat.id)); if (!refs) continue; const r = resolveRestriction(cat, selectedYMD); const sched = Array.isArray(cat.schedule) ? cat.schedule : []; // Label correcto: iniciadas vs terminadas refs.label.textContent = (String(r.labelMode) === 'iniciadas') ? 'Placas iniciadas en:' : 'Placas terminadas en:'; // Estado if (r.kind === 'na'){ refs.value.textContent = 'NO APLICA'; refs.pill.classList.remove('pz-horariopill--yellow'); refs.pill.classList.add('pz-horariopill--gray'); refs.pillHours.innerHTML = `
Sin restricción
`; refs.cd.style.display = 'none'; refs.cdText.textContent = ''; continue; } if (r.kind === 'unknown'){ refs.value.textContent = 'SIN INFORMACIÓN'; refs.pill.classList.remove('pz-horariopill--yellow'); refs.pill.classList.add('pz-horariopill--gray'); refs.pillHours.innerHTML = `
Sin información
`; refs.cd.style.display = 'none'; refs.cdText.textContent = ''; continue; } // Restricción normal refs.value.textContent = String(r.value); refs.pill.classList.remove('pz-horariopill--gray'); refs.pill.classList.add('pz-horariopill--yellow'); refs.pillHours.innerHTML = scheduleHtml(sched); // Countdown sólo si HOY y estamos en horario activo const canCount = isToday && r.kind === 'digits' && Array.isArray(sched) && sched.length > 0; if (!canCount){ refs.cd.style.display = 'none'; refs.cdText.textContent = ''; continue; } const st = getHorarioEstado(sched, nowMin); if (st.kind !== 'active'){ refs.cd.style.display = 'none'; refs.cdText.textContent = ''; continue; } const hm = countdownHM(st.endMin, nowMin); refs.cd.style.display = 'flex'; refs.cdText.textContent = `Termina en: ${hm.h}h ${pad2(hm.m)}m`; } } // Redirección por ciudad (patrón /slug/) elCity.addEventListener('change', () => { const nextId = String(elCity.value || '').trim(); const url = safeUrl(nextId ? `${baseUrl}${nextId}/` : ''); if (url) window.location.href = url; }); elPrevDay.addEventListener('click', () => { selectedYMD = addDaysYMD(selectedYMD, -1); updateHeader(); render(); }); elNextDay.addEventListener('click', () => { selectedYMD = addDaysYMD(selectedYMD, +1); updateHeader(); render(); }); function openPicker(){ if (typeof elDateInput.showPicker === 'function') elDateInput.showPicker(); else elDateInput.focus(); } elDateCenter.addEventListener('click', openPicker); elDateCenter.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' '){ e.preventDefault(); openPicker(); } }); elDateInput.addEventListener('change', () => { if (elDateInput.value){ selectedYMD = elDateInput.value; updateHeader(); render(); } }); updateHeader(); updateFooter(); render(); root._pzTimer && clearInterval(root._pzTimer); root._pzTimer = setInterval(() => { updateHeader(); updateDynamic(); }, 30 * 1000); } })();
Redacción Seguros Bolívar

¡Hola! Somos el equipo de redacción. A través de nuestros artículos también queremos brindar tranquilidad y enriquecer la vida con integridad. Por eso, aquí ofrecemos contenidos educativos, recomendaciones, noticias y mucho más. Bienvenidos todos los lectores y las empresas que desean aprender y actualizarse con información de calidad.

Recent Posts

¿Cuánto es el aumento del arriendo en el 2026?

De acuerdo con la ley 820 de 2003, el aumento del arriendo lo determina la inflación y el IPC del…

9 January, 2026

Guía de prevención general para empresas en el 2026

Prepare su empresa para el 2026 con esta guía de prevención, así protegerá su negocio frente a riesgos y garantizará…

9 January, 2026

Seguros Bolívar destaca entre las aseguradoras líderes en sostenibilidad a nivel mundial

Según el CSA 2025, Seguros Bolívar se ubica entre los 12 mejores puntajes en sostenibilidad a nivel mundial en el…

8 January, 2026

Cómo el Seguro para copropiedades le acompaña en los retos del año nuevo

Descubra cómo el Seguro para Copropiedades le ayuda a enfrentar los retos del año nuevo con confianza y protección.

8 January, 2026

Evite el sedentarismo en vacaciones y priorice su salud

Consejos prácticos para evitar el sedentarismo y cuidar su bienestar físico y mental durante las vacaciones.

6 January, 2026

Exámenes médicos para iniciar el 2026

¡Comience el año cuidando su salud! Estos son algunos exámenes de rigor que se debe realizar. ¡Tome lápiz y papel!

6 January, 2026