Estandarizar formatos de fecha mezclados en un CSV y listar los valores no interpretados
Convierte varios formatos de fecha conocidos a valores con formato ISO YYYY-MM-DD sin modificar el CSV original. Los valores que no coinciden con un formato autorizado o que no representan fechas válidas del calendario se escriben en un archivo de revisión separado.
Contenido revisado 2026.09.20Incluye código de ejemplo
Ver el índice
La traducción se ha realizado con IA. Comprueba el código, las unidades y los valores junto con el original. La revisión por hablantes nativos de cada idioma aún no se ha completado. English
Para quién esEsta guía está dirigida a personas que reciben archivos CSV con varios formatos de fecha y necesitan un proceso de limpieza repetible que no interprete silenciosamente valores ambiguos.
Preparación
Python 3.12 y un comando de terminal que inicie esa versión.
Un editor de texto que pueda guardar archivos CSV en UTF-8.
Una carpeta de trabajo donde el script pueda crear una nueva carpeta dentro de outputs.
Solo se requiere la biblioteca estándar de Python: csv, datetime y pathlib.
01Definir los formatos de fecha aceptados antes de analizarlos
La limpieza de fechas es más segura cuando los formatos aceptados se definen explícitamente. Este ejemplo reconoce seis formatos numéricos y convierte todos los valores interpretados correctamente a YYYY-MM-DD. No utiliza detección automática de fechas.
Ejemplo de entrada aceptada
Significado
Formato de Python
2026-09-01
año-mes-día
%Y-%m-%d
09/02/2026
mes/día/año
%m/%d/%Y
2026/09/03
año/mes/día
%Y/%m/%d
04-09-2026
día-mes-año
%d-%m-%Y
2026.09.05
año.mes.día
%Y.%m.%d
20260906
añomesdía
%Y%m%d
La interpretación de los formatos con barras y guiones es una decisión de política. En este tutorial, 09/02/2026 significa 2 de septiembre de 2026, mientras que 04-09-2026 significa 4 de septiembre de 2026. Si tu fuente utiliza otras convenciones, cambia los formatos aceptados antes de procesar datos reales.
02Crear un pequeño CSV sintético
El siguiente conjunto de datos es sintético y fue creado específicamente para este artículo. Guárdalo como mixed_dates.csv en tu carpeta de trabajo. Contiene seis fechas válidas en seis formatos aceptados diferentes y dos valores que deberían fallar.
csv
record_id,event_date,note
R001,2026-09-01,ISO format
R002,09/02/2026,US slash format
R003,2026/09/03,year first with slashes
R004,04-09-2026,day first with hyphens
R005,2026.09.05,dot separated
R006,20260906,compact numeric
R007,2026-02-30,invalid calendar date
R008,Sep 7 2026,unsupported text format
Hay 8 registros de datos. Los primeros 6 deberían interpretarse correctamente. R007 utiliza un patrón reconocido, pero el 30 de febrero no es una fecha real. R008 utiliza un nombre de mes en texto que se ha excluido intencionadamente de la lista de formatos aceptados.
03Calcular manualmente los valores limpios esperados
Los primeros seis valores representan fechas consecutivas desde el 1 hasta el 6 de septiembre de 2026. Por tanto, deberían producir seis valores estandarizados. Los dos registros restantes deberían conservar el texto original en el archivo de revisión.
record_id
Valor original
Valor estandarizado esperado
Estado
R001
2026-09-01
2026-09-01
PARSED
R002
09/02/2026
2026-09-02
PARSED
R003
2026/09/03
2026-09-03
PARSED
R004
04-09-2026
2026-09-04
PARSED
R005
2026.09.05
2026-09-05
PARSED
R006
20260906
2026-09-06
PARSED
R007
2026-02-30
UNPARSED
R008
Sep 7 2026
UNPARSED
Por tanto, los recuentos esperados son 8 registros totales, 6 registros interpretados y 2 registros no interpretados. El CSV limpio seguirá conteniendo los 8 registros; las filas no interpretadas tendrán un valor standardized_date vacío y el estado UNPARSED.
04Analizar los formatos autorizados y escribir nuevos archivos
Guarda el siguiente script como date_format_cleanup.py. Lee el CSV original, escribe una copia completa y limpia y también crea un CSV de revisión más pequeño que contiene únicamente los registros cuya fecha no pudo estandarizarse.
python
import csv
from datetime import datetime
from pathlib import Path
SOURCE = Path("mixed_dates.csv")
OUTPUT_DIR = Path("outputs") / "date_cleanup_result"
CLEANED = OUTPUT_DIR / "cleaned_dates.csv"
UNPARSED = OUTPUT_DIR / "unparsed_dates.csv"
DATE_COLUMN = "event_date"
FORMATS = (
"%Y-%m-%d",
"%m/%d/%Y",
"%Y/%m/%d",
"%d-%m-%Y",
"%Y.%m.%d",
"%Y%m%d",
)
def parse_date(value: str) -> str | None:
text = value.strip()
for format_string in FORMATS:
try:
parsed = datetime.strptime(text, format_string)
except ValueError:
continue
return parsed.strftime("%Y-%m-%d")
return None
def main() -> None:
if not SOURCE.is_file():
raise FileNotFoundError(f"Source CSV not found: {SOURCE}")
OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
OUTPUT_DIR.mkdir() # Stop rather than reuse an existing result folder.
with SOURCE.open("r", encoding="utf-8-sig", newline="") as source:
reader = csv.DictReader(source)
if reader.fieldnames is None:
raise ValueError("CSV has no header row.")
if DATE_COLUMN not in reader.fieldnames:
raise ValueError(f"Missing required column: {DATE_COLUMN}")
if "standardized_date" in reader.fieldnames or "date_status" in reader.fieldnames:
raise ValueError("Output column name already exists in source CSV.")
cleaned_fields = [*reader.fieldnames, "standardized_date", "date_status"]
review_fields = ["row_number", *reader.fieldnames]
with CLEANED.open("x", encoding="utf-8", newline="") as cleaned_stream, \
UNPARSED.open("x", encoding="utf-8", newline="") as review_stream:
cleaned_writer = csv.DictWriter(cleaned_stream, fieldnames=cleaned_fields)
review_writer = csv.DictWriter(review_stream, fieldnames=review_fields)
cleaned_writer.writeheader()
review_writer.writeheader()
total = 0
parsed_count = 0
unparsed_count = 0
for row_number, row in enumerate(reader, start=2):
total += 1
original_value = row[DATE_COLUMN]
standardized = parse_date(original_value)
output_row = dict(row)
if standardized is None:
output_row["standardized_date"] = ""
output_row["date_status"] = "UNPARSED"
review_writer.writerow({"row_number": row_number, **row})
unparsed_count += 1
else:
output_row["standardized_date"] = standardized
output_row["date_status"] = "PARSED"
parsed_count += 1
cleaned_writer.writerow(output_row)
if parsed_count + unparsed_count != total:
raise RuntimeError("Record counts do not balance.")
print(f"Processed {total} records.")
print(f"Parsed: {parsed_count}; unparsed: {unparsed_count}.")
print(f"Cleaned CSV: {CLEANED.as_posix()}")
print(f"Review CSV: {UNPARSED.as_posix()}")
if __name__ == "__main__":
main()
text
python date_format_cleanup.py
05Comparar los archivos de salida esperados
cleaned_dates.csv debería contener las tres columnas originales más standardized_date y date_status. Los 8 registros originales deben seguir presentes. Las dos filas fallidas no se eliminan.
record_id
event_date
standardized_date
date_status
R001
2026-09-01
2026-09-01
PARSED
R002
09/02/2026
2026-09-02
PARSED
R003
2026/09/03
2026-09-03
PARSED
R004
04-09-2026
2026-09-04
PARSED
R005
2026.09.05
2026-09-05
PARSED
R006
20260906
2026-09-06
PARSED
R007
2026-02-30
UNPARSED
R008
Sep 7 2026
UNPARSED
unparsed_dates.csv debería contener exactamente 2 filas de datos. Como el encabezado del CSV original corresponde físicamente a la línea 1, R007 está en la fila 8 del CSV y R008 en la fila 9.
row_number
record_id
event_date
8
R007
2026-02-30
9
R008
Sep 7 2026
06Comprobar los recuentos y la salida de consola
La salida de consola esperada que aparece a continuación se obtuvo manualmente a partir de la entrada sintética y del script. No es un registro capturado de una ejecución.
Confirma que cleaned_dates.csv contenga 8 filas de datos y no solamente las 6 filas procesadas correctamente.
Confirma que las fechas estandarizadas vayan desde 2026-09-01 hasta 2026-09-06 para R001 a R006.
Confirma que R007 y R008 tengan valores standardized_date vacíos y estado UNPARSED.
Confirma que unparsed_dates.csv contenga exactamente R007 y R008.
Ejecuta el script de nuevo sin cambiar OUTPUT_DIR. Debería detenerse con FileExistsError en lugar de sobrescribir el resultado anterior.
07Reconocer errores comunes
Síntoma
Qué comprobar
FileNotFoundError
Comprueba SOURCE y el directorio de trabajo actual del terminal.
Falta una columna obligatoria
Confirma que el encabezado contenga exactamente event_date o cambia DATE_COLUMN de forma deliberada.
Demasiados valores no interpretados
Revisa las convenciones de la fuente y añade únicamente formatos que estén documentados y no sean ambiguos para ese conjunto de datos.
Mes y día aparecen invertidos inesperadamente
Comprueba si las fechas con barras o guiones utilizan primero el mes o el día antes de añadir su formato.
FileExistsError
La carpeta de resultados ya existe. Revísala y selecciona un nuevo destino de salida en lugar de sobrescribirla.
Una fecha aparentemente válida sigue fallando
Los caracteres pueden contener espacios, utilizar un formato no incluido en FORMATS o representar una fecha de calendario inválida.
No respondas a una tasa elevada de fallos añadiendo numerosos formatos especulativos. Eso puede convertir datos incorrectos detectables en fechas interpretadas erróneamente sin aviso.
08Comprender los límites antes de escalar el proceso
Este ejemplo solo maneja fechas de calendario. No interpreta horas, zonas horarias, fechas seriales de Excel, nombres de meses localizados, años de dos dígitos ni valores que contengan texto descriptivo adicional. Esos casos necesitan reglas explícitas.
datetime.strptime valida las fechas del calendario, por lo que se rechaza un valor estructuralmente plausible como 2026-02-30. Esto es útil, pero una fecha interpretada correctamente no demuestra que el valor de origen sea objetivamente correcto. Un error tipográfico como 2026-09-12 en lugar de 2026-09-21 sigue siendo una fecha válida del calendario.
El orden de FORMATS importa cuando dos patrones aceptados pueden interpretar una misma cadena de forma diferente. Un proceso de limpieza de producción debería documentar la convención de la fuente, conservar el valor original, informar de los fallos y revisar los formatos ambiguos en lugar de depender únicamente del análisis automático.
Registro de ejecución y verificación
ChatGPT Python tool · Python 3.13.5 · standard library csv/datetime/pathlib
La ejecución principal imprimió exactamente: 'Processed 8 records.', 'Parsed: 6; unparsed: 2.', 'Cleaned CSV: outputs/date_cleanup_result/cleaned_dates.csv' y 'Review CSV: outputs/date_cleanup_result/unparsed_dates.csv'.
cleaned_dates.csv contenía los 8 registros de origen junto con standardized_date y date_status.
R001 a R006 se analizaron exactamente como 2026-09-01 a 2026-09-06.
El valor 2026-02-30 de R007 fue rechazado por ser una fecha de calendario no válida y se escribió con standardized_date vacío y estado UNPARSED.
El valor 'Sep 7 2026' de R008 fue rechazado porque no coincide con ninguno de los formatos aprobados y se escribió con standardized_date vacío y estado UNPARSED.
unparsed_dates.csv contenía exactamente 2 filas de datos: R007 con row_number 8 y R008 con row_number 9.
El hash SHA-256 de mixed_dates.csv permaneció sin cambios después del procesamiento, confirmando que el archivo de origen no se modificó.
Una segunda ejecución con outputs/date_cleanup_result ya existente produjo FileExistsError antes de sobrescribir los resultados anteriores.
Límites de la verificación
No se realizaron pruebas de estrés específicas sobre nombres de meses dependientes de la configuración regional, horas, zonas horarias, fechas seriales de Excel, años de dos dígitos, estructuras CSV dañadas, fallos de permisos, fallos de escritura parcial ni archivos muy grandes.
Fuentes de referencia
Las explicaciones y los ejemplos son de elaboración propia. Puedes consultar los comportamientos y conceptos relacionados en las siguientes fuentes oficiales.
Encuentra filas con coincidencia exacta en las cuatro columnas y recopílalas en un nuevo archivo de Excel junto con sus números de fila originales. Revisa cada grupo de duplicados, incluida su primera aparición.
Valida las fechas y las horas de trabajo de un CSV y después guarda los recuentos mensuales de registros y las horas totales como un CSV y un gráfico de barras.
Antes de convertir una tabla en texto, alinea el objeto, el periodo, el denominador y la unidad. Usa una tabla sintética de resultados mensuales para comprobar un valor que cambió de 80 % a 82 % y una conversión de unidades, y después crea una lista de comprobación reutilizable.
Combina hojas de cálculo que utilizan las mismas columnas en una única tabla de Excel, registrando de qué hoja procede cada fila. Un pequeño libro sintético permite verificar manualmente el orden de las filas, los recuentos y los totales.