Especialização em Sistemas com Java 21 LTS • Capítulo 07


🎯 Visão Geral dos Exemplos Práticos

Neste laboratório prático, implementaremos exemplos corporativos completos explorando:

  1. Motor de Vencimento e Juros de Boletos: ajuste automático para dias úteis bancários com TemporalAdjusters e cálculo de atraso com ChronoUnit.
  2. Central de Operações Globais (NOC Multi-Fuso): conversão de carimbos Instant em horários locais para polos internacionais (SP, Nova York, Londres e Tóquio).
  3. Formatação Thread-Safe e Internacionalizada: parsing e formatação padrão ISO e formato nacional brasileiro.

💻 Exemplo 01 — Calculadora Financeira de Vencimentos e Juros de Mora

package com.empresa.tempo;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;

/**
 * Calculadora bancária de faturamento e liquidação em dias úteis.
 */
public class CalculadoraBoletoBancario {

    public static final DateTimeFormatter FORMATO_BR = DateTimeFormatter.ofPattern("dd/MM/yyyy");
    public static final BigDecimal MULTA_ATRASO_PERCENTUAL = new BigDecimal("0.02"); // 2%
    public static final BigDecimal JUROS_DIARIOS_PERCENTUAL = new BigDecimal("0.00033"); // 0.033% ao dia

    public record TituloCobranca(
            String idTitulo,
            BigDecimal valorNominal,
            LocalDate dataEmissao,
            LocalDate dataVencimentoOriginal
    ) {
        public LocalDate calcularVencimentoUtil() {
            DayOfWeek dia = dataVencimentoOriginal.getDayOfWeek();
            if (dia == DayOfWeek.SATURDAY) {
                return dataVencimentoOriginal.plusDays(2); // Prorroga para Segunda
            } else if (dia == DayOfWeek.SUNDAY) {
                return dataVencimentoOriginal.plusDays(1); // Prorroga para Segunda
            }
            return dataVencimentoOriginal;
        }

        public BigDecimal calcularValorLiquidacao(LocalDate dataPagamento) {
            LocalDate vencimentoEfetivo = calcularVencimentoUtil();
            if (!dataPagamento.isAfter(vencimentoEfetivo)) {
                return valorNominal; // Sem encargos
            }

            long diasAtraso = ChronoUnit.DAYS.between(vencimentoEfetivo, dataPagamento);
            BigDecimal multa = valorNominal.multiply(MULTA_ATRASO_PERCENTUAL);
            BigDecimal juros = valorNominal.multiply(JUROS_DIARIOS_PERCENTUAL).multiply(new BigDecimal(diasAtraso));

            return valorNominal.add(multa).add(juros).setScale(2, RoundingMode.HALF_UP);
        }
    }

    public static void main(String[] args) {
        System.out.println("================================================================================");
        System.out.println("         SISTEMA BANCÁRIO: CÁLCULO DE VENCIMENTOS E MORA (JAVA 21)              ");
        System.out.println("================================================================================");

        // Título que vence em um Domingo
        LocalDate emissao = LocalDate.of(2026, 10, 1);
        LocalDate vencimentoDomingo = LocalDate.of(2026, 10, 18); // Domingo
        TituloCobranca titulo = new TituloCobranca("FAT-2026-9081", new BigDecimal("1500.00"), emissao, vencimentoDomingo);

        LocalDate vencimentoAjustado = titulo.calcularVencimentoUtil();

        System.out.printf("📄 Título: %s | Valor: R$ %.2f%n", titulo.idTitulo(), titulo.valorNominal());
        System.out.printf("   Emissão              : %s%n", titulo.dataEmissao().format(FORMATO_BR));
        System.out.printf("   Vencimento Nominal   : %s (%s)%n", 
                titulo.dataVencimentoOriginal().format(FORMATO_BR), titulo.dataVencimentoOriginal().getDayOfWeek());
        System.out.printf("   Vencimento Efetivo   : %s (%s) [Prorrogação Bancária]%n%n", 
                vencimentoAjustado.format(FORMATO_BR), vencimentoAjustado.getDayOfWeek());

        // Simulação 1: Pagamento na segunda-feira prorrogada (Isento de encargos)
        LocalDate dataPgto1 = LocalDate.of(2026, 10, 19);
        BigDecimal valor1 = titulo.calcularValorLiquidacao(dataPgto1);
        System.out.printf("1. Pagamento em %s: R$ %.2f [SEM ENCARGOS]%n", dataPgto1.format(FORMATO_BR), valor1);

        // Simulação 2: Pagamento com 10 dias de atraso após a data prorrogada
        LocalDate dataPgto2 = LocalDate.of(2026, 10, 29);
        BigDecimal valor2 = titulo.calcularValorLiquidacao(dataPgto2);
        System.out.printf("2. Pagamento em %s (10d atraso): R$ %.2f [MULTA + JUROS]%n", dataPgto2.format(FORMATO_BR), valor2);
        System.out.println("================================================================================");
    }
}

💻 Exemplo 02 — Central de Operações de Rede com Múltiplos Fusos Horários

package com.empresa.tempo;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

/**
 * Matriz de sincronização horária de eventos críticos em polos internacionais.
 */
public class CentralOperacoesGlobal {

    public static final DateTimeFormatter FORMATO_LOG = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss (z)");

    public record PoloOperacional(String regiao, ZoneId fuso) {}

    public static void main(String[] args) {
        System.out.println("================================================================================");
        System.out.println("        CENTRAL DE COMANDO NOC: DISPERSÃO HORÁRIA DE INCIDENTE CRÍTICO         ");
        System.out.println("================================================================================");

        // Momento do incidente registrado em tempo universal de máquina (UTC)
        Instant instanteAlertaUtc = Instant.now();
        System.out.printf("⚠️  INCIDENTE REGISTRADO (UTC Instant): %s%n%n", instanteAlertaUtc);

        List<PoloOperacional> centros = List.of(
            new PoloOperacional("Brasil (São Paulo / B3)", ZoneId.of("America/Sao_Paulo")),
            new PoloOperacional("EUA (Nova York / NYSE)", ZoneId.of("America/New_York")),
            new PoloOperacional("Reino Unido (Londres / LSE)", ZoneId.of("Europe/London")),
            new PoloOperacional("Japão (Tóquio / TSE)", ZoneId.of("Asia/Tokyo"))
        );

        System.out.printf("%-26s | %-18s | %s%n", "CENTRO DE COMANDO", "FUSO HORÁRIO", "HORÁRIO LOCAL DO EVENTO");
        System.out.println("---------------------------+--------------------+---------------------------------");

        for (PoloOperacional polo : centros) {
            ZonedDateTime horaLocal = instanteAlertaUtc.atZone(polo.fuso());
            System.out.printf("%-26s | %-18s | %s%n",
                    polo.regiao(), polo.fuso().getId(), horaLocal.format(FORMATO_LOG));
        }
        System.out.println("================================================================================");
    }
}

📋 Resultado Esperado no Console

================================================================================
         SISTEMA BANCÁRIO: CÁLCULO DE VENCIMENTOS E MORA (JAVA 21)              
================================================================================
📄 Título: FAT-2026-9081 | Valor: R$ 1500.00
   Emissão              : 01/10/2026
   Vencimento Nominal   : 18/10/2026 (SUNDAY)
   Vencimento Efetivo   : 19/10/2026 (MONDAY) [Prorrogação Bancária]

1. Pagamento em 19/10/2026: R$ 1500.00 [SEM ENCARGOS]
2. Pagamento em 29/10/2026 (10d atraso): R$ 1534.95 [MULTA + JUROS]
================================================================================

================================================================================
        CENTRAL DE COMANDO NOC: DISPERSÃO HORÁRIA DE INCIDENTE CRÍTICO         
================================================================================
⚠️  INCIDENTE REGISTRADO (UTC Instant): 2026-10-15T18:30:00Z

CENTRO DE COMANDO          | FUSO HORÁRIO       | HORÁRIO LOCAL DO EVENTO
---------------------------+--------------------+---------------------------------
Brasil (São Paulo / B3)    | America/Sao_Paulo  | 15/10/2026 15:30:00 (-03)
EUA (Nova York / NYSE)     | America/New_York   | 15/10/2026 14:30:00 (EDT)
Reino Unido (Londres / LSE)| Europe/London      | 15/10/2026 19:30:00 (BST)
Japão (Tóquio / TSE)       | Asia/Tokyo         | 16/10/2026 03:30:00 (JST)
================================================================================