[CS] CARLOS SEIDL EN
Artigos

Evite DateTime.Now em produção .NET 10 — UTC na nuvem (Azure/AWS), contêineres e práticas

Armazene em UTC e exiba em horário local: TimeProvider, contêineres com TZ=Etc/UTC e práticas para Azure, AWS e Kubernetes.

Por 4 min de leitura
  • .NET 10
  • UTC
  • Contêineres
  • Cloud
Capa do artigo: Evite DateTime.Now em produção

Fonte do projeto: https://github.com/ceseidl/dotnet10-utc-docker

Por que evitar DateTime.Now em sistemas distribuídos

Em sistemas distribuídos, especialmente hospedados em nuvem (Azure/AWS) e executados em contêineres, usar DateTime.Now pode parecer inofensivo — mas é uma armadilha. Ele depende do fuso horário local do servidor, o que causa inconsistências entre instâncias, problemas em horário de verão (DST) e dificulta testes.

⚠️ O problema com DateTime.Now

  • Dependência do horário local do servidor
    Se o servidor estiver com um fuso diferente do esperado, seus dados podem ficar inconsistentes.
  • Ambientes com múltiplos fusos horários
    Apps globais (usuários em vários países) sofrem quando cada servidor usa horários locais distintos.
  • Mudanças de horário de verão (DST)
    Quando o horário muda, cálculos baseados em DateTime.Now podem gerar resultados incorretos.
  • Testes e depuração complicados
    Simular cenários com fusos diferentes se torna um pesadelo.

Exemplo real
Um sistema de pagamentos com usuários na Índia, Emirados Árabes e EUA apresentou transações fora de ordem porque servidores (ou réplicas) registravam eventos com horários locais diferentes.

Assista ao vídeo

O bug de horário que só aparece no horário de verão
Vídeo no canal ForgeTechPit: O bug de horário que só aparece no horário de verão

A solução: usar UTC de ponta a ponta no .NET

Armazene em UTC. Exiba em Local.

  • Use DateTime.UtcNow ou TimeProvider.GetUtcNow() no código.
  • Banco e eventos sempre em UTC.
  • Converta para local apenas na UI (browser/app).
  • Configure o contêiner com TZ=Etc/UTC e tzdata.

DateTime.Now na nuvem (Azure/AWS) e em contêineres

Arquiteturas em Azure e AWS com AKS/EKS e ACA/ECS/Fargate escalam horizontalmente; réplicas podem rodar em regiões diferentes. Se cada instância usa hora local, surgem inconsistências. Portanto:

  • Padronize TZ no contêiner: ENV TZ=Etc/UTC + instalação de tzdata (quando necessário).
  • Aplique UTC no código: nunca dependa de DateTime.Now.
  • Logs e métricas: sempre em UTC; converta só na visualização (dashboard).
  • Mensageria (ASB/SQS/Kinesis): carimbe eventos em UTC.

Arquitetura recomendada para trabalhar com UTC

  • Cliente (Web/Mobile): exibe horário local do usuário.
  • API .NET 10 (pods/containers): roda com TZ=Etc/UTC, usa TimeProvider/UtcNow.
  • Serviços (Azure/AWS): mensageria, bancos e observabilidade em UTC.
  • Regra: Store in UTC. Display in Local. Testes com TimeProvider/FakeTimeProvider.

Estrutura do projeto de exemplo com TimeProvider

Destaques:

  • src/ApiUtc/ → Minimal API com TimeProvider.
  • Dockerfile / Dockerfile.alpine → Ubuntu/Alpine com TZ=Etc/UTC.
  • docker-compose.yml → Porta 8080 + healthcheck.
  • k8s/deployment.yml → AKS/EKS com TZ padronizado.
  • .github/workflows/ci.yml → CI/CD (build .NET 10 + push no GHCR via GITHUB_TOKEN).
  • README.md → artigo + diagrama + referências.

Código do projeto: Minimal API, Dockerfile e CI

1) src/ApiUtc/ApiUtc.csproj (target .NET 10)

2) src/ApiUtc/Program.cs (Minimal API + TimeProvider)

Observação: com top‑level statements, as declarações de tipos devem vir depois.

3) Dockerfile (Ubuntu 24.04 “Noble” + TZ=Etc/UTC)

4) Dockerfile.alpine (Alpine + TZ=Etc/UTC)

5) docker-compose.yml

6) k8s/deployment.yml (AKS/EKS)

7) .github/workflows/ci.yml (Opção A — GHCR com GITHUB_TOKEN)

Pontos-chave para evitar DateTime.Now em ambientes distribuídos

  • Padronize TZ no contêiner: ENV TZ=Etc/UTC + tzdata.
  • Aplique UTC no código: DateTime.UtcNow, DateTimeOffset (offset explícito) e NodaTime para regras complexas.
  • Mensageria e jobs: timestamps sempre em UTC.
  • Observabilidade: logs/métricas/traces em UTC (enriqueça com X-User-Timezone para dashboards).
  • Conversão local só na UI: pegue o timezone do usuário e converta no front.

Como executar o exemplo com Docker e Kubernetes

Docker (Ubuntu/Alpine)

Kubernetes (AKS/EKS)

GHCR (após CI)

Referências rápidas sobre .NET 10, contêineres e TimeProvider

Resumo: por que adotar UTC e evitar DateTime.Now

Evitar DateTime.Now é essencial para sistemas globais e distribuídos.
Adotar UTC em código, contêineres e infraestrutura evita bugs, simplifica testes e garante consistência.

Leia também

Quer uma revisão da arquitetura do seu backend .NET? Conheça o serviço de backend e arquitetura .NET.

← todos os artigos