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 archivos 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.
Se contaron manualmente 8 registros de origen, con 6 interpretaciones correctas esperadas y 2 fallos esperados.
Se convirtieron manualmente las seis entradas aceptadas a fechas estandarizadas consecutivas desde 2026-09-01 hasta 2026-09-06.
Se confirmó mediante razonamiento de calendario que 2026-02-30 es inválida y que Sep 7 2026 no coincide con ningún formato incluido en FORMATS.
Se obtuvieron manualmente los números de fila 8 y 9 del archivo de revisión a partir de las posiciones de línea en el CSV de origen.
Se revisó el código para confirmar que el CSV original solo se lee, que los resultados se escriben en una carpeta separada y que una carpeta de resultados ya existente hace que la ejecución se detenga.
Se obtuvieron manualmente los recuentos esperados de registros procesados, interpretados y no interpretados, así como los mensajes de consola.
Límites de la verificación
El código no fue ejecutado por el autor de esta respuesta; no se crearon archivos CSV de salida.
No se probaron nombres de meses dependientes de la configuración regional, valores de hora, zonas horarias, fechas seriales de Excel, años de dos dígitos ni archivos muy grandes.
Las convenciones de fecha aceptadas son políticas de ejemplo y deben cambiarse si la fuente real utiliza significados diferentes.
Las URL de la documentación oficial se proporcionaron a partir de ubicaciones de documentación conocidas, pero no se comprobaron en línea.
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.