Pico y Placa Barbosa

`; } }); /* FIX del error "Cannot access 'pad2' before initialization": pad2 como function (hoisting) para que nunca falle en Elementor. */ function pad2(n){ return String(n).padStart(2,'0'); } 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 ymdFromUTCNoon(dt){ return `${dt.getUTCFullYear()}-${pad2(dt.getUTCMonth()+1)}-${pad2(dt.getUTCDate())}`; } function addDaysUTCNoon(dt, delta){ const x = new Date(dt.getTime()); x.setUTCDate(x.getUTCDate() + delta); return x; } function addDaysYMD(ymd, delta){ const dt = dateUTCNoonFromYMD(ymd); dt.setUTCDate(dt.getUTCDate() + delta); return ymdFromUTCNoon(dt); } 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 scheduleLabel(schedule){ const slots = Array.isArray(schedule) ? schedule : []; const parts = slots .filter(s => s && s.start && s.end) .map(s => `${time24to12ES(String(s.start))} a ${time24to12ES(String(s.end))}`); return parts.length ? parts.join(' · ') : 'Sin restricción'; } function getHorarioEstado(schedule, nowMin){ const slots = (Array.isArray(schedule) ? schedule : []) .map(s => ({ s: toMinutes(String(s.start||'')), e: toMinutes(String(s.end||'')) })) .filter(x => x.s !== null && x.e !== null) .sort((a,b) => a.s - b.s); if (!slots.length) return { kind:'none', endMin:null }; for (const slot of slots){ if (nowMin >= slot.s && nowMin < slot.e) return { kind:'active', endMin: slot.e }; } 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 : ''; } function dayKeyFromYMD(ymd){ const dt = dateUTCNoonFromYMD(ymd); return ['sun','mon','tue','wed','thu','fri','sat'][dt.getUTCDay()]; } function iconSvg(key){ const common = `width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true"`; const car = ``; const truck = ``; const moto = ``; const k = String(key||'').toLowerCase(); if (k.includes('carga')) return truck; if (k.includes('moto')) return moto; return car; } /* ===== Festivos Colombia (para aplicar “NO APLICA” en festivos) ===== */ const _holCache = new Map(); function easterUTCNoon(year){ // Algoritmo gregoriano (Meeus/Jones/Butcher) const a = year % 19; const b = Math.floor(year / 100); const c = year % 100; const d = Math.floor(b / 4); const e = b % 4; const f = Math.floor((b + 8) / 25); const g = Math.floor((b - f + 1) / 3); const h = (19*a + b - d - g + 15) % 30; const i = Math.floor(c / 4); const k = c % 4; const l = (32 + 2*e + 2*i - h - k) % 7; const m = Math.floor((a + 11*h + 22*l) / 451); const month = Math.floor((h + l - 7*m + 114) / 31); // 3=Mar, 4=Abr const day = ((h + l - 7*m + 114) % 31) + 1; return new Date(Date.UTC(year, month-1, day, 12, 0, 0)); } function nextMondayUTCNoon(dt){ const x = new Date(dt.getTime()); const dow = x.getUTCDay(); // 0 dom ... 1 lun ... 6 sáb const add = (dow === 1) ? 0 : ((8 - dow) % 7); x.setUTCDate(x.getUTCDate() + add); return x; } function colombiaHolidaysYMD(year){ if (_holCache.has(year)) return _holCache.get(year); const out = new Set(); const mk = (m,d) => `${year}-${pad2(m)}-${pad2(d)}`; // Fijos [ [1,1],[5,1],[7,20],[8,7],[12,8],[12,25] ].forEach(([m,d]) => out.add(mk(m,d))); // Emiliani (se pasan al lunes) const emil = [ [1,6], // Reyes [3,19], // San José [6,29], // San Pedro y San Pablo [8,15], // Asunción [10,12], // Raza [11,1], // Todos los Santos [11,11] // Independencia de Cartagena ]; emil.forEach(([m,d]) => { const moved = nextMondayUTCNoon(new Date(Date.UTC(year, m-1, d, 12, 0, 0))); out.add(ymdFromUTCNoon(moved)); }); // Pascua y derivados const easter = easterUTCNoon(year); // Jueves y Viernes Santo (no se mueven) out.add(ymdFromUTCNoon(addDaysUTCNoon(easter, -3))); out.add(ymdFromUTCNoon(addDaysUTCNoon(easter, -2))); // Ascensión (easter+39) -> lunes out.add(ymdFromUTCNoon(nextMondayUTCNoon(addDaysUTCNoon(easter, 39)))); // Corpus Christi (easter+60) -> lunes out.add(ymdFromUTCNoon(nextMondayUTCNoon(addDaysUTCNoon(easter, 60)))); // Sagrado Corazón (easter+68) -> lunes out.add(ymdFromUTCNoon(nextMondayUTCNoon(addDaysUTCNoon(easter, 68)))); const arr = Array.from(out); _holCache.set(year, arr); return arr; } function isHolidayCO(ymd){ const y = Number(String(ymd).slice(0,4)); if (!y || y < 1900) return false; return colombiaHolidaysYMD(y).includes(ymd); } function inRangeYMD(ymd, start, end){ return String(ymd) >= String(start) && String(ymd) <= String(end); } function resolveRestriction(city, cat, ymd){ const rule = cat.rule || {}; const dk = dayKeyFromYMD(ymd); if (rule.type === "no_aplica"){ return { value:"NO APLICA", kind:"na", note: String(rule.note||"").trim(), schedule: [] }; } if (rule.type === "amva_weekday_pairs"){ // No aplica fines de semana if (dk === "sat" || dk === "sun"){ return { value:"NO APLICA", kind:"na", note:"Fin de semana: no aplica.", schedule: [] }; } // No aplica festivos (Colombia) if (isHolidayCO(ymd)){ return { value:"NO APLICA", kind:"na", note:"Festivo: no aplica.", schedule: [] }; } // Rango(s) de suspensión const ranges = Array.isArray(rule.no_aplica_ranges) ? rule.no_aplica_ranges : []; for (const r of ranges){ if (r && r.start && r.end && inRangeYMD(ymd, r.start, r.end)){ return { value:"NO APLICA", kind:"na", note: String(r.note||"Medida suspendida.").trim(), schedule: [] }; } } // Rotación normal (lunes a viernes) const v = rule.weekdays && rule.weekdays[dk] ? String(rule.weekdays[dk]) : ""; if (!v) return { value:"SIN INFORMACIÓN", kind:"unknown", note:"No hay dato cargado para esta fecha.", schedule: [] }; return { value:v, kind:"digits", note:"", schedule: cat.schedule || [] }; } return { value:"SIN INFORMACIÓN", kind:"unknown", note:"", schedule: [] }; } 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 options = (city.switch && Array.isArray(city.switch.options)) ? city.switch.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'); const elPrevPage = $('.pz-page--prev'); const elNextPage = $('.pz-page--next'); 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; let pageIndex = 0; let pageSize = window.matchMedia('(min-width: 820px)').matches ? 4 : 2; let visibleNodes = new Map(); function getSortedCats(){ return (Array.isArray(city.categories) ? city.categories : []) .slice() .sort((a,b) => (Number(a.priority||99) - Number(b.priority||99)) || String(a.name||'').localeCompare(String(b.name||''), 'es')); } 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; } // Footer: SOLO fuente (como pediste) function updateFooter(){ const srcName = String(city.source && city.source.name ? city.source.name : '').trim(); const srcUrl = safeUrl(city.source && city.source.url ? city.source.url : ''); if (srcUrl) elFoot.innerHTML = `Fuente: ${srcName || 'Fuente oficial'}`; else elFoot.textContent = srcName ? `Fuente: ${srcName}` : ''; } function calcPageSize(){ return window.matchMedia('(min-width: 820px)').matches ? 4 : 2; } function renderPage(){ const cats = getSortedCats(); const newSize = calcPageSize(); if (newSize !== pageSize) { pageSize = newSize; pageIndex = 0; } const totalPages = Math.max(1, Math.ceil(cats.length / pageSize)); pageIndex = Math.min(pageIndex, totalPages - 1); const start = pageIndex * pageSize; const pageCats = cats.slice(start, start + pageSize); const showPager = cats.length > pageSize; elPrevPage.style.display = showPager ? 'flex' : 'none'; elNextPage.style.display = showPager ? 'flex' : 'none'; elGrid.innerHTML = pageCats.map(cat => `
${iconSvg(cat.icon || cat.id)}
${String(cat.name || cat.id).toUpperCase()}
--
--
Termina en: --
Horario: --
`).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'), band: card.querySelector('.pz-band'), bandText: card.querySelector('.pz-bandtext'), note: card.querySelector('.pz-note') }); }); updateCardsDynamic(); } function updateCardsDynamic(){ const nowP = nowPartsInTZ(tz); todayTZ = ymdFromParts(nowP); const nowMin = minutesOfDayFromParts(nowP); const isTodaySelected = (selectedYMD === todayTZ); const cats = getSortedCats(); for (const cat of cats){ const refs = visibleNodes.get(String(cat.id)); if (!refs) continue; const r = resolveRestriction(city, cat, selectedYMD); const sched = Array.isArray(r.schedule) && r.schedule.length ? r.schedule : (Array.isArray(cat.schedule) ? cat.schedule : []); const schedLabel = scheduleLabel(sched); refs.value.classList.remove('pz-value--na'); const baseLabel = (r.kind === 'digits' && String(cat.plate_label||'').trim()) ? String(cat.plate_label).trim() : 'Placas terminadas en:'; if (r.kind === 'na'){ refs.label.textContent = 'Estado:'; refs.value.textContent = 'NO APLICA'; refs.value.classList.add('pz-value--na'); } else if (r.kind === 'unknown'){ refs.label.textContent = 'Estado:'; refs.value.textContent = 'SIN INFORMACIÓN'; } else { refs.label.textContent = baseLabel; refs.value.textContent = String(r.value); } if (r.kind === 'na' || r.kind === 'unknown'){ refs.band.classList.remove('pz-band--yellow'); refs.band.classList.add('pz-band--gray'); refs.bandText.textContent = 'Sin restricción'; } else { refs.band.classList.remove('pz-band--gray'); refs.band.classList.add('pz-band--yellow'); refs.bandText.textContent = schedLabel; } const note = String(r.note || '').trim(); if (note){ refs.note.style.display = 'block'; refs.note.textContent = note; } else { refs.note.style.display = 'none'; refs.note.textContent = ''; } const canCount = isTodaySelected && 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`; } elTime.textContent = `Hora: ${fmtTimeNoSeconds(tz)}`; } // Redirección por ciudad elCity.addEventListener('change', () => { const nextId = elCity.value; const target = options.find(o => String(o.id) === String(nextId)); const url = target && target.url ? String(target.url).trim() : ''; if (safeUrl(url)) window.location.href = url; }); elPrevDay.addEventListener('click', () => { selectedYMD = addDaysYMD(selectedYMD, -1); pageIndex = 0; updateHeader(); renderPage(); }); elNextDay.addEventListener('click', () => { selectedYMD = addDaysYMD(selectedYMD, +1); pageIndex = 0; updateHeader(); renderPage(); }); 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; pageIndex = 0; updateHeader(); renderPage(); } }); elPrevPage.addEventListener('click', () => { const cats = getSortedCats(); const totalPages = Math.max(1, Math.ceil(cats.length / pageSize)); pageIndex = (pageIndex - 1 + totalPages) % totalPages; renderPage(); }); elNextPage.addEventListener('click', () => { const cats = getSortedCats(); const totalPages = Math.max(1, Math.ceil(cats.length / pageSize)); pageIndex = (pageIndex + 1) % totalPages; renderPage(); }); window.addEventListener('resize', renderPage); updateHeader(); updateFooter(); renderPage(); root._pzTimer && clearInterval(root._pzTimer); root._pzTimer = setInterval(() => { updateHeader(); updateCardsDynamic(); }, 30 * 1000); } })();
miguel

Recent Posts

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 enero, 2026

Evite el sedentarismo en vacaciones con estos ejercicios

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

6 enero, 2026

¿Cómo saber si tengo multas de tránsito? Le explicamos cómo consultarlas

Le contamos cómo verificar si tiene una multa, comparendo o infracción de tránsito. ¡No deje que estas sorpresas lo tomen…

5 enero, 2026

Pico y placa para en Bogotá y otras ciudades de Colombia

El Pico y Placa busca mejorar el tráfico y la movilidad en las ciudades. Conozca aquí los cambios de esta…

2 enero, 2026

¿Qué es un certificado financiero? Definición y ventajas clave

Descubra qué es un certificado financiero, cómo funciona y por qué es una opción segura para hacer crecer su dinero…

31 diciembre, 2025

Guía para elegir los seguros ideales en 2026

¡Asegure su tranquilidad! Encuentre el seguro ideal para sus planes y disfrute sin preocupaciones con Seguros Bolívar.

29 diciembre, 2025