Padronize formatos de data misturados em um CSV e liste os valores não interpretados
Converta vários formatos de data conhecidos em valores no padrão ISO YYYY-MM-DD sem alterar o CSV original. Valores que não correspondem a um formato aprovado ou que não representam datas válidas do calendário são gravados em um arquivo separado para revisão.
Conteúdo verificado 2026.09.20Inclui arquivos de exemplo
Ver o sumário
A tradução foi feita com IA. Confira o código, as unidades e os valores junto com o original. A revisão por falantes nativos de cada idioma ainda não foi concluída. English
Para quem éEste guia é voltado a pessoas que recebem arquivos CSV com vários formatos de data e precisam de um processo de limpeza repetível que não tente adivinhar silenciosamente valores ambíguos.
Preparação
Python 3.12 e um comando de terminal que inicie essa versão.
Um editor de texto capaz de salvar arquivos CSV em UTF-8.
Uma pasta de trabalho em que o script possa criar uma nova pasta dentro de outputs.
É necessária apenas a biblioteca padrão do Python: csv, datetime e pathlib.
01Defina os formatos de data aceitos antes da interpretação
A limpeza de datas é mais segura quando os formatos aceitos são definidos explicitamente. Este exemplo reconhece seis formatos numéricos e converte todos os valores interpretados com sucesso para YYYY-MM-DD. Ele não usa detecção automática de datas.
Exemplo de entrada aceita
Significado
Formato do Python
2026-09-01
ano-mês-dia
%Y-%m-%d
09/02/2026
mês/dia/ano
%m/%d/%Y
2026/09/03
ano/mês/dia
%Y/%m/%d
04-09-2026
dia-mês-ano
%d-%m-%Y
2026.09.05
ano.mês.dia
%Y.%m.%d
20260906
anomêsdia
%Y%m%d
A interpretação de formatos com barras e hífens é uma decisão de política. Neste tutorial, 09/02/2026 significa 2 de setembro de 2026, enquanto 04-09-2026 significa 4 de setembro de 2026. Se sua fonte usar convenções diferentes, altere os formatos aceitos antes de processar dados reais.
02Crie um pequeno CSV sintético
O conjunto de dados a seguir é sintético e foi criado especificamente para este artigo. Salve-o como mixed_dates.csv na pasta de trabalho. Ele contém seis datas válidas em seis formatos aceitos diferentes e dois valores que devem falhar.
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
Há 8 registros de dados. Os primeiros 6 devem ser interpretados com sucesso. R007 usa um padrão reconhecido, mas 30 de fevereiro não é uma data real. R008 usa um nome de mês em texto que foi intencionalmente excluído da lista de formatos aceitos.
03Calcule manualmente os valores limpos esperados
Os primeiros seis valores representam datas consecutivas de 1 a 6 de setembro de 2026. Portanto, eles devem produzir seis valores padronizados. Os dois registros restantes devem manter o texto original no arquivo de revisão.
record_id
Valor original
Valor padronizado esperado
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
Portanto, os totais esperados são 8 registros, 6 registros interpretados e 2 registros não interpretados. O CSV limpo ainda conterá todos os 8 registros; as linhas não interpretadas receberão um valor standardized_date vazio e o status UNPARSED.
04Interprete os formatos aprovados e grave novos arquivos
Salve o script a seguir como date_format_cleanup.py. Ele lê o CSV original, grava uma cópia limpa completa e também cria um CSV de revisão menor contendo apenas os registros cuja data não pôde ser padronizada.
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
05Compare os arquivos de saída esperados
cleaned_dates.csv deve conter as três colunas originais mais standardized_date e date_status. Todos os 8 registros originais continuam presentes. As duas linhas com falha não são excluídas.
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 deve conter exatamente 2 linhas de dados. Como o cabeçalho do CSV original é fisicamente a linha 1, R007 corresponde à linha 8 do CSV e R008 à linha 9.
row_number
record_id
event_date
8
R007
2026-02-30
9
R008
Sep 7 2026
06Verifique as contagens e a saída do console
A saída esperada do console abaixo foi derivada manualmente a partir da entrada sintética e do script. Não é um log capturado de execução.
Confirme que cleaned_dates.csv contém 8 linhas de dados, e não apenas as 6 linhas processadas com sucesso.
Confirme que as datas padronizadas vão de 2026-09-01 a 2026-09-06 para R001 a R006.
Confirme que R007 e R008 têm valores standardized_date vazios e status UNPARSED.
Confirme que unparsed_dates.csv contém exatamente R007 e R008.
Execute o script novamente sem alterar OUTPUT_DIR. Ele deve parar com FileExistsError em vez de sobrescrever o resultado anterior.
07Reconheça erros comuns
Sintoma
O que verificar
FileNotFoundError
Verifique SOURCE e o diretório de trabalho atual do terminal.
Coluna obrigatória ausente
Confirme que o cabeçalho contém exatamente event_date ou altere DATE_COLUMN deliberadamente.
Muitos valores não interpretados
Inspecione as convenções da fonte e adicione apenas formatos documentados e não ambíguos para esse conjunto de dados.
Mês e dia aparecem invertidos inesperadamente
Verifique se datas com barras ou hífens usam primeiro o mês ou o dia antes de adicionar o formato correspondente.
FileExistsError
A pasta de resultados já existe. Revise-a e selecione um novo destino de saída em vez de sobrescrevê-la.
Uma data aparentemente válida ainda falha
Os caracteres podem conter espaços, usar um formato não listado em FORMATS ou representar uma data de calendário inválida.
Não responda a uma alta taxa de falhas adicionando muitos formatos especulativos. Isso pode transformar dados inválidos detectáveis em datas interpretadas incorretamente sem aviso.
08Entenda os limites antes de ampliar o uso
Este exemplo trata apenas de datas de calendário. Ele não interpreta horários, fusos horários, datas seriais do Excel, nomes de meses localizados, anos com dois dígitos nem valores que contenham texto descritivo adicional. Esses casos precisam de regras explícitas.
datetime.strptime valida datas do calendário, portanto um valor estruturalmente plausível como 2026-02-30 é rejeitado. Isso é útil, mas uma data interpretada com sucesso não prova que o valor de origem esteja factualmente correto. Um erro de digitação como 2026-09-12 em vez de 2026-09-21 ainda é uma data válida do calendário.
A ordem de FORMATS importa quando dois padrões aceitos podem interpretar a mesma string de formas diferentes. Um processo de limpeza em produção deve documentar a convenção da fonte, preservar o valor original, relatar falhas e revisar formatos ambíguos em vez de depender apenas da interpretação automática.
Foram contados manualmente 8 registros de origem, com 6 interpretações bem-sucedidas esperadas e 2 falhas esperadas.
As seis entradas aceitas foram convertidas manualmente em datas padronizadas consecutivas de 2026-09-01 a 2026-09-06.
Foi confirmado por raciocínio de calendário que 2026-02-30 é inválida e que Sep 7 2026 não corresponde a nenhum formato listado em FORMATS.
Os números das linhas de revisão 8 e 9 foram derivados manualmente a partir das posições das linhas no CSV de origem.
O código foi inspecionado para confirmar que o CSV original é apenas lido, que os resultados são gravados em uma pasta separada e que uma pasta de resultados já existente faz a execução parar.
As contagens esperadas de registros processados, interpretados e não interpretados, bem como as mensagens do console, foram derivadas manualmente.
Limites da verificação
O código não foi executado pelo autor desta resposta; nenhum arquivo CSV de saída foi criado.
Nomes de meses dependentes de localidade, valores de horário, fusos horários, datas seriais do Excel, anos com dois dígitos e arquivos muito grandes não foram testados.
As convenções de data aceitas são políticas de exemplo e devem ser alteradas se a fonte real usar significados diferentes.
As URLs da documentação oficial foram fornecidas com base em locais de documentação conhecidos, mas não foram verificadas ao vivo.
Encontre linhas com correspondência exata nas quatro colunas e reúna-as em um novo arquivo do Excel junto com seus números de linha originais. Revise cada grupo de duplicatas, incluindo a primeira ocorrência.
Antes de transformar uma tabela em texto, alinhe o objeto, o período, o denominador e a unidade. Use uma tabela sintética de resultados mensais para verificar um valor que mudou de 80 % para 82 % e uma conversão de unidades, e depois monte uma lista de verificação reutilizável.
Combine planilhas que usam as mesmas colunas em uma única tabela do Excel, registrando de qual planilha cada linha veio. Uma pequena pasta de trabalho sintética permite verificar manualmente a ordem das linhas, as contagens e os totais.