🔝To translate this blog post to your language, select it in the top left Google box.
En el artículo anterior publiqué un reloj analógico que mostraba manualmente la hora actual, presionando la tecla para calcular el libro, ya que no era automático por no tener macros:
Tenía la ventaja de poder usarlo en Excel para la Web y de insertarlo en el blog, por si alguien quería probarlo sin tener instalado Excel.
Ahora publico un reloj analógico en tiempo real, que automáticamente mueve la aguja de los segundos cada segundo, gracias a las macros VBA.
NOTA: Haga clic en el reloj analógico para iniciar o detener el temporizador de un segundo.
ACTUALIZACIÓN v1.10: Descarga la última actualización para elegir el tipo de reloj con un desplegable en la celda D6:
- 12 Horas: De 1 a 12 horas.
- 24 Horas: De 12 a 23 horas si la hora es después del mediodía. Si es antes del mediodía se comporta como el reloj de 12 Horas.
- AM/PM: De 1 a 12 horas, indicando AM o PM si la hora es antes o después del mediodía, respectivamente.
Con la última actualización el tipo de reloj que se muestra ya no está determinado por la configuración regional del sistema operativo, sino que depende de la elección del usuario.
Descarga el reloj analógico en tiempo real desde este enlace:
Las macros del archivo descargado están bloqueadas por defecto. Para desbloquear las macros debes modificar las Propiedades del archivo siguiendo estas instrucciones:
Abre el archivo y presiona el botón: Habilitar edición cuando aparezca el aviso de VISTA PROTEGIDA.
Presiona el botón: Habilitar contenido cuando aparezca la ADVERTENCIA DE SEGURIDAD Las macros se han deshabilitado o se deshabilitó parte del contenido activo.
Las macros no están protegidas para poder analizar fácilmente el código VBA. Las hojas están protegidas sin contraseña, para poder analizarlas y para evitar que los usuarios alteren las fórmulas.
ATENCIÓN: Se puede modificar este libro de Excel respetando esta licencia:
Creative Commons — Atribución-NoComercial-CompartirIgual-No portada — CC BY-NC-SA 4.0
ACTUALIZACIÓN v1.10: He ajustado el comportamiento del módulo con la ayuda de la IA de Microsoft, llevando a cabo pruebas y corrección de errores en 10 iteraciones durante su desarrollo.
¡Realmente me lo he pasado "pipa" interactuando con la IA de Microsoft Copilot!
Este es el resultado de la décima revisión del módulo:
A continuación explicaré cuáles son las principales características del reloj analógico en tiempo real.
⭐ Módulo del temporizador API
Este módulo implementa un temporizador de alta precisión en
Excel usando las funciones de Windows SetTimer y
KillTimer.
Está diseñado para funcionar en 32 y 64 bits, sincronizarse exactamente con el reloj del sistema y actualizar el rango nombrado "miHora" cada segundo sin bloquear Excel.
🧩 1. Compatibilidad total 32/64 bits
Incluye declaraciones API con #If VBA7 Then para:
SetTimerKillTimer
Esto garantiza compatibilidad con:
- Excel 2010 (32/64 bits)
- Excel 2013 → Excel para Microsoft 365
- Windows 7 → Windows 11
⏱️ 2. Temporizador real basado en Windows (no OnTime)
A diferencia de Application.OnTime, este temporizador:
- No se retrasa cuando Excel está ocupado
- No se acumula
- No depende del motor de Excel
- No se rompe al cerrar libros
- Tiene precisión de milisegundos
Usa:
TimerID = SetTimer(0, 0, intervalo_ms, AddressOf Tick)
🎯 3. Sincronización exacta con el segundo del sistema
El módulo calcula cuántos milisegundos faltan para el próximo segundo exacto:
msRestantes = 1000 - (CLng(Timer * 1000) Mod 1000)
If msRestantes = 1000 Then msRestantes = 0
Esto permite:
- Primer tick comienza exactamente en 0 ms, no en un punto arbitrario
- Ticks posteriores cada 1000 ms exactos
- Evitar desvíos acumulados
🔄 4. Crea el temporizador API
RelojTimerID = SetTimer(0, 0, msRestantes, AddressOf Tick)
El primer tick ocurre justo al inicio del siguiente segundo.
🧱 5. Protección contra errores (incluido el 50290)
El procedimiento Tick incluye:
On Error GoTo SalirTick
Esto evita que:
- Excel detenga el temporizador si está ocupado
- Se produzcan errores al cerrar libros
- El temporizador quede “colgado”
🖱️ 6. Cursor dinámico de reloj
Al iniciar:
Application.Cursor = xlWait
Al detener:
Application.Cursor = xlDefault
Esto da feedback visual claro al usuario.
🛑 7. Inicio/parada conmutado
El módulo incluye un interruptor:
Sub IniciarDetenerReloj()
que alterna entre:
IniciarRelojDetenerReloj
Ideal para botones o accesos rápidos.
🧹 8. Limpieza segura del temporizador
Al detener:
KillTimer 0, TimerID
Esto garantiza:
- No quedan temporizadores huérfanos
- No se ejecutan ticks después de cerrar el libro
- No se producen cierres inesperados de Excel
📌 9. No bloquea Excel
El temporizador API:
- No usa bucles
- No usa
DoEvents - No congela la interfaz
- Permite trabajar en otros libros sin interferencias
🎁 Resumen del Temporizador API
Temporizador API de alta precisión para Excel, compatible con 32/64 bits, sincronizado al segundo del sistema, con actualización inteligente, cursor dinámico, protección contra errores y control seguro de inicio/parada. No bloquea Excel y mantiene estabilidad incluso con múltiples libros abiertos.
A continuación explico técnicamente el módulo
ModTempo, desarrollado con la ayuda de la IA.
La estructura sigue el flujo real del código y explica qué hace, por qué lo hace, y cómo interactúan entre sí las partes críticas: API Win32, temporizador, corrección de deriva (drift correction), modo híbrido y escritura inteligente.
⭐ Módulo Temporizador
ModTempo implementa un
reloj de alta precisión dentro de Excel usando:
-
Temporizador API de Windows (
SetTimer) - Corrección de deriva (drift correction)
-
Modo híbrido:
- Actualiza la celda solo si Excel está activo y visible
- Ahorra CPU cuando Excel está en segundo plano
- Milisegundos reales mediante
Now() - Escritura inteligente: solo escribe si el valor cambia
- Sin interferir con cálculos ni edición
El resultado es un reloj estable, eficiente y preciso,
superior a cualquier implementación basada en
Application.OnTime.
🧩 Declaraciones API
El módulo declara cuatro funciones de la API de Windows:
1) SetTimer
Crea un temporizador que ejecuta un procedimiento (callback) cada X milisegundos.
-
hWnd = 0→ temporizador asociado al proceso, no a una ventana nIDEvent = 0→ Windows asigna un ID automáticamenteuElapse→ intervalo en milisegundos-
lpTimerFunc→ dirección del procedimientoTick
Devuelve un ID de temporizador, que se guarda en
RelojTimerID.
2) KillTimer
Detiene el temporizador usando su ID.
3) IsWindowVisible
Comprueba si la ventana de Excel está visible (no minimizada).
4) GetForegroundWindow
Devuelve el handle de la ventana que tiene el foco.
🧩 Variables locales
Private RelojCorriendo As Boolean
Private RelojTimerID As LongPtr
Private TickUltimo As Double
- RelojCorriendo: estado ON/OFF del reloj.
- RelojTimerID: ID del temporizador API (obligatorio LongPtr en 64 bits).
- TickUltimo: marca de tiempo del último tick para medir drift.
Son Private porque solo deben ser accesibles dentro del
módulo.
🧩 IniciarDetenerReloj
Alterna el estado del reloj:
- Si estaba apagado → lo inicia
- Si estaba encendido → lo detiene
Es un interruptor simple para el usuario.
🧩 IniciarReloj
Este procedimiento:
1) Cambia el cursor a reloj de arena
Indica visualmente que el reloj se está activando.
2) Marca el reloj como activo
RelojCorriendo = True
3) Guarda el tiempo actual
TickUltimo = Timer
4) Sincroniza el primer tick con el siguiente segundo exacto
msRestantes = 1000 - (CLng(Timer * 1000) Mod 1000)
If msRestantes = 1000 Then msRestantes = 0
Esto garantiza que el reloj empiece exactamente en 00 ms, no en un punto arbitrario.
5) Crea el temporizador API
RelojTimerID = SetTimer(0, 0, msRestantes, AddressOf Tick)
El primer tick ocurre justo al inicio del siguiente segundo.
🧩 DetenerReloj
- Marca el reloj como detenido
- Mata el temporizador API
- Restaura el cursor
Evita fugas de temporizadores y el cursor indica que el reloj se ha detenido.
🧩 Tick — el corazón del reloj
Este procedimiento se ejecuta cada segundo (o antes, si hay corrección de deriva - drift correction).
1) Protección contra cálculos
If Application.CalculationState <> xlDone Then GoTo SalirTick
Evita interferir con cálculos pesados.
2) Determina si Excel está activo
ExcelActivo = (GetForegroundWindow() = hWndExcel) _
And (IsWindowVisible(hWndExcel) <> 0)
Esto implementa el modo híbrido:
- Si Excel está activo → modo precisión
- Si Excel está en segundo plano → modo ahorro
3) Si Excel no está activo → no escribir
If Not ExcelActivo Then GoTo SalirTick
Evita repintados innecesarios.
4) Si la hoja no está visible → no escribir
If Not ActiveSheet.Visible Then GoTo SalirTick
Evita trabajo inútil.
5) Corrección de deriva (drift correction)
tNow = Timer
delta = tNow - TickUltimo
If delta < 0 Then delta = delta + 86400#
Si el temporizador se desvía más de ±50 ms:
If Abs(delta - 1#) > 0.05 Then
KillTimer 0, RelojTimerID
RelojTimerID = SetTimer(0, 0, 1000, AddressOf Tick)
End If
Esto reancla el temporizador al segundo real del sistema.
6) Leer hora con milisegundos reales
vHora = Now()
Excel sí soporta milisegundos en formato personalizado.
7) Escritura inteligente
If celda.Value2 <> vHora Then
Solo escribe si el valor cambia → evita repintados.
8) Escritura optimizada
Application.EnableEvents = False
Application.ScreenUpdating = False
celda.Value2 = vHora
Application.ScreenUpdating = True
Application.EnableEvents = True
Evita parpadeos y eventos innecesarios.
🧩 Comportamiento final del módulo
- Se sincroniza con el siguiente segundo exacto.
-
Cada tick:
- Comprueba si Excel está activo
- Comprueba si la hoja está visible
- Comprueba si Excel está calculando
- Mide drift y corrige si es necesario
- Lee la hora con milisegundos
- Escribe solo si cambia el valor
El módulo ModTempo consigue:
- Sincronización exacta con el segundo real
- Milisegundos reales en cada actualización
- Corrección automática de deriva
- Ahorro de energía cuando Excel está en segundo plano
- Cero parpadeos
- Cero interferencias con cálculos
- Cero repintados innecesarios
- Compatibilidad total con 32 y 64 bits
Es un reloj de precisión profesional dentro de Excel:
- Preciso
- Eficiente
- Estable
- Sin jitter
- Sin parpadeos
- Sin interferir con Excel
🧩DIAGRAMA DE FLUJO—ModTempo
┌──────────────────────────┐
│ IniciarDetenerReloj() │
└──────────────┬───────────┘
│
¿RelojCorriendo = False? ────┤
│Sí
▼
┌────────────────────┐
│ IniciarReloj() │
└───────┬────────────┘
│
▼
┌──────────────────────────────┐
│ Cursor = xlWait │
│ RelojCorriendo = True │
│ TickUltimo = Timer │
└───────────┬──────────────────┘
│
▼
┌────────────────────────────────────────┐
│ Calcular msRestantes hasta el próximo │
│ segundo exacto │
└───────────────┬────────────────────────┘
│
▼
┌────────────────────────────────────────┐
│ SetTimer( intervalo = msRestantes ) │
│ → llama a Tick() │
└────────────────────────────────────────┘
──────────────────────────────────────────────────────────────────────────────
TICK() — Cada disparo del temporizador
──────────────────────────────────────────────────────────────────────────────
┌────────────────────────────────────────┐
│ Tick() │
└───────────┬────────────────────────────┘
│
▼
┌────────────────────────────────────────┐
│ Continua si Excel no está calculando, │
│ si está activo y si la hoja es visible │
└───────────┬────────────────────────────┘
│Sí
▼
┌────────────────────────────────────────┐
│ Mide la deriva del temporizador │
│ KillTimer (primer timer) │
│ SetTimer( intervalo = 1000 ms ) │
│ (sincronizado al segundo exacto) │
└───────────┬────────────────────────────┘
│
▼
┌────────────────────────────────────────┐
│ Lee día y hora: vHora = Now() │
└───────────┬────────────────────────────┘
│
¿Cambió la hora? ─────┤
│Sí
▼
┌────────────────────────────────────────┐
│ Range("miHora") = vHora │
└────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────┐
│ Fin Tick() │
└────────────────────────────────────────┘
──────────────────────────────────────────────────────────────────────────────
STOP — Detener temporizador
──────────────────────────────────────────────────────────────────────────────
┌──────────────────────────┐
│ DetenerReloj() │
└──────────────┬───────────┘
│
▼
┌──────────────────────────┐
│ RelojCorriendo = False │
│ KillTimer TimerID │
│ Cursor = xlDefault │
└──────────────────────────┘
🎯 Qué representa este diagrama
- El flujo completo desde que el usuario inicia o detiene el temporizador.
- La sincronización exacta al segundo del sistema.
- El comportamiento del primer tick (sincronización) y los siguientes (intervalo fijo).
- La actualización inteligente de
miHora. - La gestión del cursor de reloj.
- La detención limpia del temporizador API.
⭐ Eventos en ThisWorkbook
Este módulo conecta el ciclo de vida del libro con el temporizador API, de modo que el temporizador:
- se inicia automáticamente cuando se abre el libro
- se detiene automáticamente cuando el libro intenta cerrarse
Así se evita que el temporizador quede activo en memoria o siga ejecutándose después de cerrar el archivo.
🧩 Comportamiento detallado
✔ Al abrir el libro
Private Sub Workbook_Open()
IniciarReloj
End Sub
Cuando el archivo se abre:
- Se ejecuta
IniciarReloj - Se activa el temporizador API
- Se inicia la sincronización al segundo exacto
- Se cambia el cursor a modo reloj (
xlWait) - Comienza la actualización periódica (Tick)
Resultado: el temporizador arranca automáticamente sin intervención del usuario.
✔ Al intentar cerrar el libro
Private Sub Workbook_BeforeClose(Cancel As Boolean)
DetenerReloj
End Sub
Cuando el usuario pulsa:
- La X
- Alt+F4
- Ctrl+W
- O cierra desde menú
Excel ejecuta Workbook_BeforeClose, que:
- Llama a
DetenerReloj - Mata el temporizador con
KillTimer - Restaura el cursor (
xlDefault) - Limpia el estado interno
Resultado: el temporizador se detiene siempre antes de que el libro se cierre, evitando temporizadores huérfanos o errores 50290.
🎯 Resumen de los eventos del reloj
Este código garantiza que el temporizador API se inicia automáticamente al abrir el libro y se detiene de forma segura al cerrarlo, evitando que quede activo en segundo plano.
⭐ Diagrama de flujo — Eventos del reloj
┌────────────────────────────┐
│ El usuario abre el libro │
└───────────────┬────────────┘
│
▼
┌──────────────────────────┐
│ Workbook_Open() │
└─────────────┬────────────┘
│
▼
┌──────────────────────────┐
│ IniciarReloj │
│ • Inicia temporizador │
│ • Activa cursor reloj │
│ • Sincroniza primer tick │
└─────────────┬────────────┘
│
▼
┌──────────────────────────┐
│ Temporizador en marcha │
│ (Tick cada segundo) │
└─────────────┬────────────┘
│
▼
┌────────────────────────────┐
│ El usuario intenta cerrar │
│ (X, Alt+F4, Ctrl+W, menú) │
└───────────────┬────────────┘
│
▼
┌──────────────────────────┐
│ Workbook_BeforeClose() │
└─────────────┬────────────┘
│
▼
┌──────────────────────────┐
│ DetenerReloj │
│ • Mata el timer API │
│ • Restaura cursor normal │
│ • Limpia estado interno │
└─────────────┬────────────┘
│
▼
┌──────────────────────────┐
│ El libro se cierra sin │
│ temporizadores activos │
└──────────────────────────┘
🎯 Qué expresa este diagrama
- El temporizador se inicia automáticamente al abrir el libro.
- El temporizador se detiene siempre antes de que el libro se cierre.
- Se evita que queden temporizadores huérfanos en memoria.
-
Se garantiza que Excel no siga ejecutando
Tickdespués del cierre. - El flujo es limpio, seguro y totalmente automático.
Administrador de nombres
Estos son los nombres definidos en el Administrador de nombres, necesarios para diseñar el Reloj analógico en tiempo real.
Selecciona tu idioma para disfrutar de una Excel-ente experiencia.
Descarga el Reloj analógico en tiempo real, pruébalo y te agradeceré cualquier sugerencia que me hagas tendente a mejorarlo.








































