2025-11-27 19:08:06 +00:00
|
|
|
from __future__ import annotations
|
2025-11-30 09:43:57 +00:00
|
|
|
|
2025-08-28 08:51:05 +00:00
|
|
|
from datetime import datetime, timezone
|
2025-11-30 09:43:57 +00:00
|
|
|
from pathlib import Path
|
2025-11-27 19:08:06 +00:00
|
|
|
import os
|
2025-11-30 09:43:57 +00:00
|
|
|
from typing import Optional
|
2025-08-28 08:51:05 +00:00
|
|
|
|
2025-11-30 09:43:57 +00:00
|
|
|
|
|
|
|
|
DEFAULT_PATH = Path("last_execution.txt")
|
|
|
|
|
DEFAULT_FALLBACK = datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
|
2025-11-27 19:08:06 +00:00
|
|
|
FMT = "%Y-%m-%d %H:%M:%S"
|
2025-08-28 08:51:05 +00:00
|
|
|
|
|
|
|
|
|
2025-11-27 19:08:06 +00:00
|
|
|
def obtener_fecha_ultima_ejecucion(
|
|
|
|
|
path: str = DEFAULT_PATH,
|
|
|
|
|
*,
|
|
|
|
|
fallback: Optional[datetime] = None,
|
|
|
|
|
) -> datetime:
|
|
|
|
|
"""
|
|
|
|
|
Lee la última fecha de ejecución desde `path` y la devuelve como aware (UTC).
|
2025-11-30 09:43:57 +00:00
|
|
|
|
|
|
|
|
- Si el fichero no existe o el contenido es inválido, devuelve `fallback`.
|
|
|
|
|
- Si `fallback` es None, usa DEFAULT_FALLBACK (2024-01-01T00:00:00Z).
|
2025-11-27 19:08:06 +00:00
|
|
|
"""
|
2025-11-30 09:43:57 +00:00
|
|
|
|
|
|
|
|
# Comentario: fallback explícito para evitar lógica duplicada en llamadas
|
|
|
|
|
effective_fallback = fallback or DEFAULT_FALLBACK
|
|
|
|
|
|
|
|
|
|
if path is None:
|
|
|
|
|
return effective_fallback
|
|
|
|
|
|
|
|
|
|
# 2. Convertimos str -> Path
|
|
|
|
|
path = Path(path)
|
2025-11-27 19:08:06 +00:00
|
|
|
|
2025-08-28 08:51:05 +00:00
|
|
|
try:
|
2025-11-30 09:43:57 +00:00
|
|
|
text = path.read_text(encoding="utf-8").strip()
|
|
|
|
|
if not text:
|
|
|
|
|
# Comentario: fichero vacío -> usamos fallback
|
|
|
|
|
return effective_fallback
|
|
|
|
|
|
|
|
|
|
dt_naive = datetime.strptime(text, FMT)
|
2025-11-27 19:08:06 +00:00
|
|
|
return dt_naive.replace(tzinfo=timezone.utc)
|
2025-08-28 08:51:05 +00:00
|
|
|
except FileNotFoundError:
|
2025-11-30 09:43:57 +00:00
|
|
|
return effective_fallback
|
2025-11-27 19:08:06 +00:00
|
|
|
except ValueError:
|
2025-11-30 09:43:57 +00:00
|
|
|
# Comentario: formato inválido en el archivo -> fallback
|
|
|
|
|
return effective_fallback
|
2025-11-27 19:08:06 +00:00
|
|
|
|
2025-08-28 08:51:05 +00:00
|
|
|
|
2025-11-27 19:08:06 +00:00
|
|
|
def actualizar_fecha_ultima_ejecucion(
|
|
|
|
|
path: str = DEFAULT_PATH,
|
|
|
|
|
*,
|
|
|
|
|
momento: Optional[datetime] = None,
|
|
|
|
|
) -> None:
|
|
|
|
|
"""
|
|
|
|
|
Escribe en `path` la fecha/hora (UTC) en formato YYYY-MM-DD HH:MM:SS.
|
|
|
|
|
Si `momento` es None, usa ahora en UTC.
|
|
|
|
|
Crea directorios intermedios si no existen.
|
|
|
|
|
"""
|
|
|
|
|
if momento is None:
|
|
|
|
|
momento = datetime.now(timezone.utc)
|
|
|
|
|
else:
|
|
|
|
|
# Normalizamos a UTC si viene con tz; si es naive, asumimos UTC
|
|
|
|
|
if momento.tzinfo is None:
|
|
|
|
|
momento = momento.replace(tzinfo=timezone.utc)
|
|
|
|
|
else:
|
|
|
|
|
momento = momento.astimezone(timezone.utc)
|
2025-08-28 08:51:05 +00:00
|
|
|
|
2025-11-27 19:08:06 +00:00
|
|
|
# Asegurar carpeta si `path` incluye directorios
|
|
|
|
|
folder = os.path.dirname(os.path.abspath(path))
|
|
|
|
|
if folder and not os.path.exists(folder):
|
|
|
|
|
os.makedirs(folder, exist_ok=True)
|
2025-08-28 08:51:05 +00:00
|
|
|
|
2025-11-27 19:08:06 +00:00
|
|
|
with open(path, "w", encoding="utf8") as f:
|
|
|
|
|
f.write(momento.strftime(FMT))
|