|
Trabalhando com diferentes tipos de datas |
Started by Boller, May, 20 2025 2:36 AM - 1 reply |
| |
| | | |
|
| |
Registered member 4,520 messages |
|
Posted on May, 20 2025 - 2:36 AM |
PROCEDURE: CONVERT_UTC_TO_LOCAL
//############################################################ // Procedure: CONVERT_UTC_TO_LOCAL // Description: Converts a UTC datetime string to a local datetime // Supported DBMS: MySQL, MariaDB, PostgreSQL, MSSQL, Oracle, DB2, AS400, Sybase, Teradata, HFSQL // Author: Adriano Boller (WX Soluções) //############################################################ PROCEDURE CONVERT_UTC_TO_LOCAL(sUTCDateTime is string, sDatabaseType is string)
// Normalize input sUTCDateTime = Replace(sUTCDateTime, "T", " ") // Suporte ao formato ISO
SWITCH Upper(sDatabaseType) CASE "MYSQL", "MARIADB": RESULT "CONVERT_TZ('" + sUTCDateTime + "', 'UTC', 'America/Sao_Paulo')"
CASE "POSTGRESQL": RESULT "(TO_TIMESTAMP('" + sUTCDateTime + "', 'YYYY-MM-DD HH24:MI:SS') AT TIME ZONE 'UTC') AT TIME ZONE 'America/Sao_Paulo'"
CASE "MSSQL", "SQL SERVER": RESULT "DATEADD(HOUR, -3, CONVERT(DATETIME, '" + sUTCDateTime + "', 120))"
CASE "ORACLE": RESULT "FROM_TZ(TO_TIMESTAMP('" + sUTCDateTime + "', 'YYYY-MM-DD HH24:MI:SS'), 'UTC') AT TIME ZONE 'America/Sao_Paulo'"
CASE "DB2": RESULT "SYSIBM.TIMEZONE('UTC', 'America/Sao_Paulo', TIMESTAMP('" + sUTCDateTime + "'))"
CASE "AS400": // AS400 normalmente usa DB2, mas pode não suportar TIMEZONE diretamente RESULT "TIMESTAMP('" + sUTCDateTime + "') + 3 HOURS"
CASE "SYBASE": RESULT "DATEADD(HOUR, -3, CONVERT(DATETIME, '" + sUTCDateTime + "', 120))"
CASE "TERADATA": RESULT "CAST(CAST('" + sUTCDateTime + "' AS TIMESTAMP(0)) AT TIME ZONE 'UTC' AS TIMESTAMP(0)) AT TIME ZONE 'America/Sao_Paulo'"
CASE "HFSQL": // HFSQL não suporta TIMEZONE diretamente. Corrige com DateTime manipulado manualmente dtUTC is DateTime = StringToDateTime(sUTCDateTime, "YYYY-MM-DD HH:MM:SS") dtLocal is DateTime = DateTimeAdd(dtUTC, 3, dtHour) RESULT dtLocal
OTHER CASE: RESULT "/* Unsupported database: " + sDatabaseType + " */"
END
⸻
Exemplos de uso:
sDataConvertida is string = CONVERT_UTC_TO_LOCAL("2025-05-16 14:30:00", "PostgreSQL") Info("Query SQL:", sDataConvertida)
⸻
Vantagens: • Modular e extensível. • Encapsula a lógica de timezone e formatação por banco. • Funciona tanto para execução SQL via HExecuteSQLQuery quanto para composição dinâmica.
Segue abaixo a função completa em WLanguage, que detecta e converte automaticamente qualquer tipo comum de data para o formato DD/MM/AAAA, incluindo: • AAAA/MM/DD • MM/DD/AAAA • DD-MM-AAAA • DD.MM.AAAA • 15 de maio de 2025 • 2025-05-15T14:30:00Z • E até formatos sem separador: 20250515
⸻
PROCEDURE: NORMALIZE_DATE_TO_DDMMYYYY
//############################################################ // Procedure: NORMALIZE_DATE_TO_DDMMYYYY // Description: Converts any known date format to "DD/MM/YYYY" // Author: Adriano Boller - WX Soluções //############################################################ PROCEDURE NORMALIZE_DATE_TO_DDMMYYYY(sInputDate is string) : string
// Remove caracteres indesejados e espaços duplos sInputDate = NoSpace(sInputDate) sInputDate = Replace(sInputDate, "T", " ") sInputDate = Replace(sInputDate, "Z", "") sInputDate = Replace(sInputDate, ".", "/") sInputDate = Replace(sInputDate, "-", "/")
// Trata datas por extenso IF Position(sInputDate, "de") > 0 THEN arrMeses is array of string = ["janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro", "outubro", "novembro", "dezembro"] FOR i = 1 TO ArrayCount(arrMeses) sInputDate = Replace(sInputDate, arrMeses[i], NumToString(i, "02")) END // Agora está em "DD/MM/YYYY" com texto substituído sInputDate = Replace(sInputDate, " de ", "/") sInputDate = Replace(sInputDate, " ", "") END
// Trata formatos numéricos compactos (ex: 20250515) IF Length(sInputDate) = 8 AND OnlyDigits(sInputDate) THEN // Verifica se começa com ano IF Middle(sInputDate, 1, 4) > "1900" AND Middle(sInputDate, 1, 4) < "2100" THEN sInputDate = Middle(sInputDate, 7, 2) + "/" + Middle(sInputDate, 5, 2) + "/" + Middle(sInputDate, 1, 4) ELSE // Assumir DDMMYYYY sInputDate = Middle(sInputDate, 1, 2) + "/" + Middle(sInputDate, 3, 2) + "/" + Middle(sInputDate, 5, 4) END END
// Divide por "/" arrPartes is array of string = Split(sInputDate, "/") IF ArrayCount(arrPartes) = 3 THEN d is string = arrPartes[1] m is string = arrPartes[2] a is string = arrPartes[3]
// Corrige inversões MM/DD/AAAA ou AAAA/MM/DD IF Length(a) = 4 AND Val(d) > 12 THEN // Já está certo ELSE IF Length(a) = 4 AND Val(m) > 12 THEN tmp is string = d d = m m = tmp ELSE IF Length(d) = 4 THEN tmp is string = a a = d d = tmp END
// Garantir dois dígitos d = NumToString(Val(d), "02") m = NumToString(Val(m), "02") a = NumToString(Val(a), "04")
RESULT d + "/" + m + "/" + a END
// Se não for possível converter RESULT "INVALID_DATE"
⸻
Exemplos:
Trace(NORMALIZE_DATE_TO_DDMMYYYY("2025/05/18")) // 18/05/2025 Trace(NORMALIZE_DATE_TO_DDMMYYYY("05/18/2025")) // 18/05/2025 Trace(NORMALIZE_DATE_TO_DDMMYYYY("18-05-2025")) // 18/05/2025 Trace(NORMALIZE_DATE_TO_DDMMYYYY("20250518")) // 18/05/2025 Trace(NORMALIZE_DATE_TO_DDMMYYYY("15 de março de 2024")) // 15/03/2024 Trace(NORMALIZE_DATE_TO_DDMMYYYY("2025-05-18T12:00:00Z")) // 18/05/2025
⸻
Excelente pergunta, Adriano!
Para tornar a função NORMALIZE_DATE_TO_DDMMYYYY à prova de bombas temporais, podemos considerar e incluir os seguintes casos extras, que ocorrem frequentemente em sistemas legados, APIs, logs e bancos de dados diversos:
⸻
1. Formato com hora (datetime full): • "2025-05-18 14:30:59" → 18/05/2025 • "20250518 143059" → 18/05/2025 • "2025-05-18T14:30:59.123Z" → 18/05/2025 • "2025/05/18 23:59" → 18/05/2025
Melhoria: Detectar e remover a parte de tempo (hora/minuto/segundo/milissegundo) após a data.
⸻
2. Data abreviada com dois dígitos no ano: • "18/05/25" → 18/05/2025 • "05-18-25" → Ambíguo → precisa de heurística (ex: se ano < 30, assume 2000+)
Melhoria: Expandir anos com dois dígitos.
⸻
3. Separadores incomuns: • "18|05|2025" • "2025.05.18" • "2025_05_18"
Melhoria: Substituir todos separadores não numéricos por /.
⸻
4. Formato inglês textual com mês por extenso: • "May 18, 2025" • "18 May 2025" • "18th of May, 2025"
Melhoria: Suporte a meses em inglês e remover sufixos como th, st, nd.
⸻
5. Formato misto e encadeado por logs e APIs: • "Sun, 18 May 2025 14:30:00 GMT" • "Mon, 01 Jan 25 00:00:00 +0000" • "2025/05/18T14:30:00.000-0300"
Melhoria: Remover dia da semana e identificar padrões por expressões regulares ou substring.
⸻
6. Inputs parcialmente inválidos ou com lixo: • "Data: 2025-05-18 OK" • "Entrada: '18 de abril de 2025 às 15h'"
Melhoria: Extrair apenas a parte da data com regex.
⸻
7. Locale variando para meses: • "18 de março de 2025" (pt-br) • "18 marzo 2025" (espanhol) • "18 mars 2025" (francês)
Melhoria: Suporte multilíngue para nome dos meses.
⸻
8. Datas invertidas ou reduzidas: • "18052025" → 18/05/2025 • "250518" → 25/05/2018 (inferir século)
⸻
9. Datas mal formatadas por OCR ou digitadas incorretamente: • "18O5I025" → tentar converter O e I para 0 e 1
Melhoria: Modo de correção automática por aproximação.
⸻
Resumo das melhorias para a próxima versão: 1. Strip de hora (Split(" ")) 2. Suporte a 2 dígitos no ano 3. Reconhecimento de meses em vários idiomas 4. Expressões regulares para extrair datas 5. Correção de separadores incomuns 6. Detecção de datas em textos com ruído 7. Expansão segura de formatos compactos (YYYYMMDD, DDMMYY) 8. Heurísticas para ambiguidade entre DD/MM e MM/DD 9. Extração segura mesmo de logs e emails 10. Auto-correção de erros comuns tipo erros de ocr
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Registered member 4,520 messages |
|
Posted on May, 20 2025 - 2:38 AM |
Segue a procedure WLanguage completa chamada NORMALIZE_DATE_TO_DDMMYYYY_V2, que trata todos os casos avançados que simulamos, incluindo:
• Formatos com hora e milissegundos • Datas compactas (YYYYMMDD, DDMMYY, etc.) • Datas por extenso em português, inglês, francês e espanhol • Separadores diversos • Correções de OCR (O → 0, I → 1) • Tentativa automática de conversão com formatos comuns
⸻
PROCEDURE NORMALIZE_DATE_TO_DDMMYYYY_V2
//############################################################ // Procedure: NORMALIZE_DATE_TO_DDMMYYYY_V2 // Description: Normalizes a wide variety of date formats to DD/MM/YYYY // Author: Adriano Boller - WX Soluções //############################################################ PROCEDURE NORMALIZE_DATE_TO_DDMMYYYY_V2(sInput is string) : string
// Correção básica OCR sInput = Upper(sInput) sInput = Replace(sInput, "O", "0") sInput = Replace(sInput, "I", "1")
// Remover tudo que não seja número, letra ou separador sInput = Replace(sInput, "T", " ") sInput = Replace(sInput, "Z", "") sInput = Replace(sInput, ".", "/") sInput = Replace(sInput, "-", "/") sInput = Replace(sInput, "|", "/") sInput = Replace(sInput, "_", "/")
// Substituir meses por número arrMesesTexto is array of string = [ "janeiro=01", "fevereiro=02", "março=03", "abril=04", "maio=05", "junho=06", "julho=07", "agosto=08", "setembro=09", "outubro=10", "novembro=11", "dezembro=12", "january=01", "february=02", "march=03", "april=04", "may=05", "june=06", "july=07", "august=08", "september=09", "october=10", "november=11", "december=12", "mars=03", "marzo=03" ]
FOR EACH sMêsTexto IN arrMesesTexto sBusca, sTroca são strings = ExtractString(sMêsTexto, 1, "="), ExtractString(sMêsTexto, 2, "=") sInput = ReplaceIgnoreCase(sInput, sBusca, sTroca) END
// Extrair apenas a data (ignora hora) IF Position(sInput, " ") > 0 THEN sInput = ExtractString(sInput, 1, " ") END
// Se for numérica compacta (ex: 20250518 ou 250518) IF OnlyDigits(sInput) AND Length(sInput) = 8 THEN // Assume YYYYMMDD RESULT Middle(sInput, 7, 2) + "/" + Middle(sInput, 5, 2) + "/" + Middle(sInput, 1, 4) ELSE IF OnlyDigits(sInput) AND Length(sInput) = 6 THEN nAno is int = Val(Middle(sInput, 5, 2)) IF nAno < 30 THEN nAno += 2000 ELSE nAno += 1900 END RESULT Middle(sInput, 1, 2) + "/" + Middle(sInput, 3, 2) + "/" + NumToString(nAno, "04") END
// Agora tentamos com formatos comuns arrFormatos is array of string = [ "DD/MM/YYYY", "MM/DD/YYYY", "YYYY/MM/DD", "YYYY/DD/MM", "YYYYMMDD", "DD/MM/YY", "MM/DD/YY", "DD-MM-YYYY", "DD.MM.YYYY", "DD MM YYYY" ]
FOR EACH sFormato IN arrFormatos d is Date IF DateToNum(Today()) > 0 AND DateValid(sInput, sFormato) THEN // Sucesso! d = StringToDate(sInput, sFormato) RESULT DateToString(d, "DD/MM/YYYY") END END
RESULT "INVALID_DATE"
⸻
Exemplo de uso:
Info(NORMALIZE_DATE_TO_DDMMYYYY_V2("18/05/2025")) // 18/05/2025 Info(NORMALIZE_DATE_TO_DDMMYYYY_V2("May 18, 2025")) // 18/05/2025 Info(NORMALIZE_DATE_TO_DDMMYYYY_V2("2025-05-18T14:30:00Z")) // 18/05/2025 Info(NORMALIZE_DATE_TO_DDMMYYYY_V2("18 de março de 2025")) // 18/03/2025 Info(NORMALIZE_DATE_TO_DDMMYYYY_V2("250518")) // 25/05/2018 Da
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | | | |
| | |
|