Trabalho Avançado com Datas e Horas em Go: dos fundamentos à otimização
No desenvolvimento web moderno, trabalhar com datas e horas é uma das tarefas mais comuns e, ao mesmo tempo, traiçoeiras. A linguagem Go (Golang) fornece o poderoso pacote time, mas seu uso avançado exige um profundo entendimento dos mecanismos internos, trabalho com fusos horários, desempenho e edge-cases. Neste artigo, abordaremos técnicas que vão além dos exemplos básicos e ajudarão você a escrever código confiável, rápido e correto para trabalhar com tempo.
1. Maestria no trabalho com time.Duration: não apenas Sleep
time.Duration não é apenas um intervalo de tempo, mas um int64 de nanossegundos. Compreender isso abre possibilidades para cálculos e comparações eficientes.
1.1. Otimização da comparação de Duration
Evite cadeias de if/else para verificar limites. Use aritmética:
package main
import ( "fmt" "time")
func main() { d := 2 * time.Second
// Ruim: cria objetos temporários if d >= time.Second && d <= 5*time.Second { fmt.Println("No intervalo") }
// Bom: comparação int64 const minNs = int64(time.Second) const maxNs = int64(5 * time.Second) if ns := int64(d); ns >= minNs && ns <= maxNs { fmt.Println("No intervalo (otimizado)") }}1.2. Arredondamento e truncamento de Duration
O método Round() arredonda para o múltiplo especificado, Truncate() descarta o resto. Isso é crítico para cache e agrupamento de dados em janelas de tempo.
now := time.Now()// Arredondar para o minuto mais próximorounded := now.Round(time.Minute)// Truncar para o início da horatruncated := now.Truncate(time.Hour)fmt.Println(rounded, truncated)2. Parsing e formatação avançados: além do RFC3339
Go usa a magic reference date: Mon Jan 2 15:04:05 MST 2006 (01/02 03:04:05PM '06 -0700). Entender essa mnemônica é a chave para criar qualquer formato.
2.1. Formatos personalizados com suporte a timezone
Frequentemente é necessário o formato "2024-03-15 14:30:00 +0300". Monte a string usando o reference layout:
layout := "2006-01-02 15:04:05 -0700"t := time.Now()fmt.Println(t.Format(layout))// Saída: 2024-03-15 14:30:00 +03002.2. Tratamento de strings não padronizadas
Se os dados de entrada têm formato variável, use vários Parse em ordem decrescente de especificidade. Para validação rigorosa, use time.ParseInLocation com localização explícita.
func parseFlexivel(s string) (time.Time, error) { layouts := []string{ time.RFC3339, "2006-01-02 15:04:05", "2006-01-02", "Jan 2, 2006", } for _, layout := range layouts { if t, err := time.Parse(layout, s); err == nil { return t, nil } } return time.Time{}, fmt.Errorf("não foi possível fazer parsing: %s", s)}2.3. Problema com "Z" e "+00:00"
RFC3339 permite ambas as variantes. time.Parse as trata corretamente, mas ao formatar, use time.RFC3339 para saída padrão.
3. Trabalho com fusos horários: evitando erros típicos
Fusos horários são a principal fonte de bugs. Go armazena time.Time como um momento absoluto (Unix timestamp) + offset. Isso torna as operações de comparação seguras, mas não intuitivas.
3.1. Carregamento de localizações do IANA Time Zone Database
Use time.LoadLocation com o nome correto do banco de dados IANA (por exemplo, "Europe/Moscow"). Nunca use abreviações (MSK, EST) — elas são ambíguas.
loc, err := time.LoadLocation("Europe/Moscow")if err != nil { log.Fa