Tutorial Java Date și Time API

LocalDate, LocalDateTime și Formatare - Ghid Complet cu Exemple

Introducere

Java 8 a introdus un nou API pentru date și timp în pachetul java.time. Acest API rezolvă multe probleme ale vechilor clase Date și Calendar, oferind o abordare mai intuitivă și thread-safe.

De ce să folosim noul API?
  • Immutabilitate - obiectele sunt thread-safe
  • API clar și intuitiv
  • Suport pentru zone orare
  • Calcule simple cu date

LocalDate - Lucrul cu Date

LocalDate reprezintă o dată fără timp și fără fus orar (ex: 2024-03-15).

Crearea unei Date

import java.time.LocalDate;
import java.time.Month;

// Data curentă
LocalDate astazi = LocalDate.now();
System.out.println("Astăzi: " + astazi);

// Dată specifică
LocalDate dataNasterii = LocalDate.of(1990, 5, 15);
System.out.println("Data nașterii: " + dataNasterii);

// Folosind enum Month
LocalDate craciun = LocalDate.of(2024, Month.DECEMBER, 25);
System.out.println("Crăciun: " + craciun);

// Parse din String
LocalDate dataText = LocalDate.parse("2024-03-15");
System.out.println("Din text: " + dataText);
Astăzi: 2024-03-15 Data nașterii: 1990-05-15 Crăciun: 2024-12-25 Din text: 2024-03-15

Extragerea Componentelor

LocalDate data = LocalDate.of(2024, 3, 15);

// Componente individuale
int an = data.getYear();
int luna = data.getMonthValue();
Month lunaEnum = data.getMonth();
int zi = data.getDayOfMonth();
int ziDinAn = data.getDayOfYear();
DayOfWeek ziDinSaptamana = data.getDayOfWeek();

System.out.println("An: " + an);
System.out.println("Luna: " + luna + " (" + lunaEnum + ")");
System.out.println("Zi: " + zi);
System.out.println("Ziua " + ziDinAn + " din an");
System.out.println("Zi din săptămână: " + ziDinSaptamana);
An: 2024 Luna: 3 (MARCH) Zi: 15 Ziua 75 din an Zi din săptămână: FRIDAY

Modificarea Datelor

LocalDate data = LocalDate.of(2024, 3, 15);

// Adăugare
LocalDate pesteSaptamana = data.plusWeeks(1);
LocalDate pesteOLuna = data.plusMonths(1);
LocalDate pesteUnAn = data.plusYears(1);
LocalDate peste45Zile = data.plusDays(45);

// Scădere
LocalDate acumOSaptamana = data.minusWeeks(1);
LocalDate acum3Luni = data.minusMonths(3);

// Modificare specifică
LocalDate primaDinLuna = data.withDayOfMonth(1);
LocalDate ultimaDinLuna = data.withDayOfMonth(data.lengthOfMonth());

System.out.println("Original: " + data);
System.out.println("Peste o săptămână: " + pesteSaptamana);
System.out.println("Prima zi din lună: " + primaDinLuna);
System.out.println("Ultima zi din lună: " + ultimaDinLuna);

LocalDateTime - Lucrul cu Date și Timp

LocalDateTime combină data și timpul fără fus orar.

Crearea LocalDateTime

import java.time.LocalDateTime;
import java.time.LocalTime;

// Data și ora curentă
LocalDateTime acum = LocalDateTime.now();
System.out.println("Acum: " + acum);

// Dată și oră specifică
LocalDateTime intalnire = LocalDateTime.of(2024, 3, 15, 14, 30, 0);
System.out.println("Întâlnire: " + intalnire);

// Combinare LocalDate cu LocalTime
LocalDate data = LocalDate.of(2024, 3, 15);
LocalTime ora = LocalTime.of(14, 30);
LocalDateTime combinat = LocalDateTime.of(data, ora);
System.out.println("Combinat: " + combinat);

// Parse din String
LocalDateTime parsed = LocalDateTime.parse("2024-03-15T14:30:00");
System.out.println("Parsed: " + parsed);

Operații cu LocalDateTime

LocalDateTime start = LocalDateTime.of(2024, 3, 15, 10, 0);

// Adăugare timp
LocalDateTime dupa2Ore = start.plusHours(2);
LocalDateTime dupa30Minute = start.plusMinutes(30);
LocalDateTime dupa3ZileSi4Ore = start.plusDays(3).plusHours(4);

// Truncare
LocalDateTime laOra = start.truncatedTo(ChronoUnit.HOURS);
LocalDateTime laMinut = start.truncatedTo(ChronoUnit.MINUTES);

System.out.println("Start: " + start);
System.out.println("După 2 ore: " + dupa2Ore);
System.out.println("După 3 zile și 4 ore: " + dupa3ZileSi4Ore);
System.out.println("Trunchiat la oră: " + laOra);

Formatare Date și Timp

DateTimeFormatter permite formatarea și parsarea flexibilă a datelor.

Formatare Standard

import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;

LocalDateTime dataOra = LocalDateTime.of(2024, 3, 15, 14, 30, 45);

// Formate predefinite
String iso = dataOra.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
String basic = dataOra.format(DateTimeFormatter.BASIC_ISO_DATE);

// Formate localizate
String scurt = dataOra.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT));
String mediu = dataOra.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
String lung = dataOra.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG));

System.out.println("ISO: " + iso);
System.out.println("Basic: " + basic);
System.out.println("Scurt: " + scurt);
System.out.println("Mediu: " + mediu);

Formatare Personalizată

LocalDateTime dataOra = LocalDateTime.now();

// Pattern-uri personalizate
DateTimeFormatter format1 = DateTimeFormatter.ofPattern("dd/MM/yyyy");
DateTimeFormatter format2 = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
DateTimeFormatter format3 = DateTimeFormatter.ofPattern("EEEE, d MMMM yyyy");
DateTimeFormatter format4 = DateTimeFormatter.ofPattern("HH:mm:ss dd.MM.yyyy");
DateTimeFormatter format5 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");

System.out.println("Format 1: " + dataOra.format(format1));
System.out.println("Format 2: " + dataOra.format(format2));
System.out.println("Format 3: " + dataOra.format(format3));
System.out.println("Format 4: " + dataOra.format(format4));
System.out.println("Format 5: " + dataOra.format(format5));

Tabel Pattern-uri Comune

Pattern Descriere Exemplu
y An 2024
M Luna în an 3 sau 03
MMM Luna abreviată Mar
MMMM Luna completă Martie
d Zi în lună 15
E Zi în săptămână Vin
EEEE Zi completă Vineri
H Ora (0-23) 14
h Ora (1-12) 2
m Minut 30
s Secundă 45
a AM/PM PM

Parsare cu Format

// Parsare cu format specific
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
String dataText = "15/03/2024 14:30";
LocalDateTime parsed = LocalDateTime.parse(dataText, formatter);
System.out.println("Parsed: " + parsed);

// Parsare flexibilă
DateTimeFormatter flexibil = DateTimeFormatter.ofPattern("[dd/MM/yyyy][dd-MM-yyyy]");
LocalDate data1 = LocalDate.parse("15/03/2024", flexibil);
LocalDate data2 = LocalDate.parse("15-03-2024", flexibil);

// Parsare cu locale specific
DateTimeFormatter romanesc = DateTimeFormatter.ofPattern("d MMMM yyyy", new Locale("ro", "RO"));
LocalDate dataRo = LocalDate.parse("15 martie 2024", romanesc);
System.out.println("Data RO: " + dataRo);

Operații Avansate

Calcularea Perioadelor

import java.time.Period;
import java.time.Duration;
import java.time.temporal.ChronoUnit;

// Period - pentru date
LocalDate nastere = LocalDate.of(1990, 5, 15);
LocalDate astazi = LocalDate.now();
Period varsta = Period.between(nastere, astazi);

System.out.println("Vârstă: " + varsta.getYears() + " ani, " + 
                   varsta.getMonths() + " luni, " + 
                   varsta.getDays() + " zile");

// Duration - pentru timp
LocalDateTime start = LocalDateTime.of(2024, 3, 15, 10, 0);
LocalDateTime end = LocalDateTime.of(2024, 3, 15, 14, 30);
Duration durata = Duration.between(start, end);

System.out.println("Durată: " + durata.toHours() + " ore și " + 
                   durata.toMinutesPart() + " minute");

// ChronoUnit pentru calcule specifice
long zile = ChronoUnit.DAYS.between(nastere, astazi);
long ore = ChronoUnit.HOURS.between(start, end);
System.out.println("Total zile de la naștere: " + zile);
System.out.println("Total ore: " + ore);

Ajustări Temporale

import java.time.temporal.TemporalAdjusters;
import java.time.DayOfWeek;

LocalDate data = LocalDate.of(2024, 3, 15);

// Prima/ultima zi din lună
LocalDate primaZi = data.with(TemporalAdjusters.firstDayOfMonth());
LocalDate ultimaZi = data.with(TemporalAdjusters.lastDayOfMonth());

// Prima/ultima zi din an
LocalDate primaZiAn = data.with(TemporalAdjusters.firstDayOfYear());
LocalDate ultimaZiAn = data.with(TemporalAdjusters.lastDayOfYear());

// Următoarea zi specifică
LocalDate urmatorulVineri = data.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
LocalDate urmatorulSauAcestaVineri = data.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY));

// Prima zi de luni din lună
LocalDate primaLuni = data.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
LocalDate ultimaLuni = data.with(TemporalAdjusters.lastInMonth(DayOfWeek.MONDAY));

System.out.println("Data originală: " + data);
System.out.println("Prima zi din lună: " + primaZi);
System.out.println("Ultima zi din lună: " + ultimaZi);
System.out.println("Următorul vineri: " + urmatorulVineri);
System.out.println("Prima luni din lună: " + primaLuni);

Comparații între Date

LocalDate data1 = LocalDate.of(2024, 3, 15);
LocalDate data2 = LocalDate.of(2024, 3, 20);
LocalDate data3 = LocalDate.of(2024, 3, 15);

// Comparații de bază
boolean esteInainte = data1.isBefore(data2);
boolean esteDupa = data1.isAfter(data2);
boolean esteEgal = data1.isEqual(data3);

System.out.println(data1 + " este înainte de " + data2 + "? " + esteInainte);
System.out.println(data1 + " este după " + data2 + "? " + esteDupa);
System.out.println(data1 + " este egal cu " + data3 + "? " + esteEgal);

// Comparație cu compareTo
int comparatie = data1.compareTo(data2);
if (comparatie < 0) {
    System.out.println(data1 + " este mai devreme");
} else if (comparatie > 0) {
    System.out.println(data1 + " este mai târziu");
} else {
    System.out.println("Datele sunt egale");
}

// Verificare an bisect
boolean bisect = Year.of(2024).isLeap();
System.out.println("2024 este an bisect? " + bisect);

Conversii și Integrare

Conversii între Tipuri

// LocalDate -> LocalDateTime
LocalDate data = LocalDate.of(2024, 3, 15);
LocalDateTime dataOraStart = data.atStartOfDay();
LocalDateTime dataOraCustom = data.atTime(14, 30);
LocalDateTime dataOraTime = data.atTime(LocalTime.of(14, 30, 0));

// LocalDateTime -> LocalDate/LocalTime
LocalDateTime dataOra = LocalDateTime.now();
LocalDate doar Data = dataOra.toLocalDate();
LocalTime doarOra = dataOra.toLocalTime();

// Conversie cu Date (legacy)
Date oldDate = new Date();
LocalDateTime fromOldDate = LocalDateTime.ofInstant(
    oldDate.toInstant(), ZoneId.systemDefault()
);

// LocalDateTime -> Date
LocalDateTime ldt = LocalDateTime.now();
Date toOldDate = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

System.out.println("LocalDate: " + data);
System.out.println("La început de zi: " + dataOraStart);
System.out.println("La ora specifică: " + dataOraCustom);

Exemple Practice

Calculator Vârstă

public static int calculeazaVarsta(LocalDate dataNasterii) {
    return Period.between(dataNasterii, LocalDate.now()).getYears();
}

// Utilizare
LocalDate nastere = LocalDate.of(1990, 5, 15);
int varsta = calculeazaVarsta(nastere);
System.out.println("Vârsta: " + varsta + " ani");

Zile Lucrătoare

public static long zileLucratoare(LocalDate start, LocalDate end) {
    return start.datesUntil(end)
        .filter(data -> {
            DayOfWeek zi = data.getDayOfWeek();
            return zi != DayOfWeek.SATURDAY && zi != DayOfWeek.SUNDAY;
        })
        .count();
}

// Utilizare
LocalDate inceput = LocalDate.of(2024, 3, 1);
LocalDate sfarsit = LocalDate.of(2024, 3, 31);
long zile = zileLucratoare(inceput, sfarsit);
System.out.println("Zile lucrătoare: " + zile);

Următoarea Zi de Plată

public static LocalDate urmatoareaPlata(int ziuaDinLuna) {
    LocalDate azi = LocalDate.now();
    LocalDate plata = azi.withDayOfMonth(
        Math.min(ziuaDinLuna, azi.lengthOfMonth())
    );
    
    if (plata.isBefore(azi) || plata.isEqual(azi)) {
        plata = plata.plusMonths(1);
        plata = plata.withDayOfMonth(
            Math.min(ziuaDinLuna, plata.lengthOfMonth())
        );
    }
    return plata;
}

// Utilizare
LocalDate plata = urmatoareaPlata(25);
System.out.println("Următoarea plată: " + plata);

Formatare Avansată - Exemple Multiple

LocalDateTime acum = LocalDateTime.now();

// Formatări pentru diferite scopuri
Map formate = new HashMap<>();
formate.put("Log", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
formate.put("Factura", DateTimeFormatter.ofPattern("dd.MM.yyyy"));
formate.put("Email", DateTimeFormatter.ofPattern("EEEE, d MMMM yyyy 'la ora' HH:mm"));
formate.put("API", DateTimeFormatter.ISO_LOCAL_DATE_TIME);
formate.put("Filename", DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
formate.put("Raport", DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"));
formate.put("Simplu", DateTimeFormatter.ofPattern("dd-MM-yyyy"));
formate.put("Complet", DateTimeFormatter.ofPattern("EEEE, d MMMM yyyy HH:mm:ss"));

System.out.println("Exemple de formatare pentru: " + acum);
System.out.println("-".repeat(50));
formate.forEach((nume, formatter) -> {
    System.out.println(nume + ": " + acum.format(formatter));
});
Atenție la Timezone!

LocalDate și LocalDateTime nu conțin informații despre fus orar. Pentru lucrul cu fusuri orare, folosiți ZonedDateTime.

Validare Date

public static boolean esteDataValida(String data, String format) {
    try {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        LocalDate.parse(data, formatter);
        return true;
    } catch (DateTimeParseException e) {
        return false;
    }
}

// Exemple de utilizare
String[] dateTest = {"29/02/2024", "30/02/2024", "31/04/2024", "15/03/2024"};
String format = "dd/MM/yyyy";

for (String data : dateTest) {
    boolean valida = esteDataValida(data, format);
    System.out.println(data + " este validă? " + valida);
}

Lucrul cu Sărbători

public class CalculatorSarbatori {
    private static final Map SARBATORI = Map.of(
        MonthDay.of(1, 1), "Anul Nou",
        MonthDay.of(5, 1), "Ziua Muncii",
        MonthDay.of(12, 25), "Crăciun",
        MonthDay.of(12, 26), "A doua zi de Crăciun"
    );
    
    public static boolean esteSarbatoare(LocalDate data) {
        return SARBATORI.containsKey(MonthDay.from(data));
    }
    
    public static String numeSarbatoare(LocalDate data) {
        return SARBATORI.getOrDefault(MonthDay.from(data), "Zi normală");
    }
    
    public static LocalDate urmatoareaSarbatoare(LocalDate de la) {
        return SARBATORI.keySet().stream()
            .map(md -> md.atYear(de la.getYear()))
            .filter(data -> data.isAfter(de la))
            .min(LocalDate::compareTo)
            .orElse(SARBATORI.keySet().stream()
                .map(md -> md.atYear(de la.getYear() + 1))
                .min(LocalDate::compareTo)
                .orElse(null));
    }
}

// Utilizare
LocalDate azi = LocalDate.now();
System.out.println("Este sărbătoare azi? " + CalculatorSarbatori.esteSarbatoare(azi));
LocalDate urmatoarea = CalculatorSarbatori.urmatoareaSarbatoare(azi);
System.out.println("Următoarea sărbătoare: " + urmatoarea);

Generator Calendar

public static void afiseazaCalendar(int an, int luna) {
    YearMonth lunaAn = YearMonth.of(an, luna);
    LocalDate primaZi = lunaAn.atDay(1);
    LocalDate ultimaZi = lunaAn.atEndOfMonth();
    
    System.out.println("\n" + lunaAn.getMonth() + " " + an);
    System.out.println("Lu Ma Mi Jo Vi Sâ Du");
    
    // Spații pentru prima săptămână
    int spatii = primaZi.getDayOfWeek().getValue() - 1;
    for (int i = 0; i < spatii; i++) {
        System.out.print("   ");
    }
    
    // Afișare zile
    LocalDate data = primaZi;
    while (!data.isAfter(ultimaZi)) {
        System.out.printf("%2d ", data.getDayOfMonth());
        if (data.getDayOfWeek() == DayOfWeek.SUNDAY) {
            System.out.println();
        }
        data = data.plusDays(1);
    }
    System.out.println();
}

// Utilizare
afiseazaCalendar(2024, 3);
Best Practices:
  • Folosiți întotdeauna noul API (java.time) în loc de Date/Calendar
  • Preferați LocalDate/LocalDateTime pentru majoritatea cazurilor
  • Folosiți ZonedDateTime doar când aveți nevoie de timezone
  • Creați constante pentru formatele folosite frecvent
  • Tratați întotdeauna excepțiile de parsare
  • Folosiți Period pentru diferențe între date, Duration pentru timp

Exemple Complexe de Formatare

// Formatare cu Locale diferite
LocalDateTime data = LocalDateTime.now();
Locale[] locales = {
    Locale.US, 
    Locale.FRANCE, 
    Locale.GERMANY,
    new Locale("ro", "RO")
};

for (Locale locale : locales) {
    DateTimeFormatter formatter = DateTimeFormatter
        .ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.SHORT)
        .withLocale(locale);
    System.out.println(locale.getDisplayName() + ": " + data.format(formatter));
}

// Formatare customizată cu text
DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern(
    "'Data:' dd MMMM yyyy 'Ora:' HH:mm:ss"
);
System.out.println(data.format(customFormatter));

// Formatare condiționată
DateTimeFormatter smartFormatter = new DateTimeFormatterBuilder()
    .appendPattern("dd/MM/yyyy")
    .optionalStart()
    .appendPattern(" HH:mm")
    .optionalEnd()
    .toFormatter();

LocalDate doar Data = LocalDate.now();
LocalDateTime dataOra = LocalDateTime.now();
System.out.println("Doar data: " + doarData.format(smartFormatter));
System.out.println("Data și ora: " + dataOra.format(smartFormatter));

Utilități Practice

public class DateTimeUtils {
    
    // Verifică dacă o dată este în weekend
    public static boolean esteWeekend(LocalDate data) {
        DayOfWeek zi = data.getDayOfWeek();
        return zi == DayOfWeek.SATURDAY || zi == DayOfWeek.SUNDAY;
    }
    
    // Calculează următoarea zi lucrătoare
    public static LocalDate urmatoareaZiLucratoare(LocalDate data) {
        LocalDate rezultat = data;
        do {
            rezultat = rezultat.plusDays(1);
        } while (esteWeekend(rezultat));
        return rezultat;
    }
    
    // Găsește toate zilele de vineri dintr-o lună
    public static List toateVinerile(YearMonth luna) {
        List vineri = new ArrayList<>();
        LocalDate data = luna.atDay(1);
        LocalDate ultima = luna.atEndOfMonth();
        
        // Găsește prima vineri
        data = data.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY));
        
        while (!data.isAfter(ultima)) {
            vineri.add(data);
            data = data.plusWeeks(1);
        }
        return vineri;
    }
    
    // Calculează timpul rămas până la un eveniment
    public static String timpRamas(LocalDateTime eveniment) {
        LocalDateTime acum = LocalDateTime.now();
        if (eveniment.isBefore(acum)) {
            return "Evenimentul a trecut";
        }
        
        Duration durata = Duration.between(acum, eveniment);
        long zile = durata.toDays();
        long ore = durata.toHoursPart();
        long minute = durata.toMinutesPart();
        
        return String.format("%d zile, %d ore, %d minute", zile, ore, minute);
    }
}

// Exemple de utilizare
LocalDate azi = LocalDate.now();
System.out.println("Este weekend? " + DateTimeUtils.esteWeekend(azi));
System.out.println("Următoarea zi lucrătoare: " + 
    DateTimeUtils.urmatoareaZiLucratoare(azi));

YearMonth lunaCurenta = YearMonth.now();
System.out.println("Vineri în această lună: " + 
    DateTimeUtils.toateVinerile(lunaCurenta));

LocalDateTime eveniment = LocalDateTime.of(2024, 12, 25, 0, 0);
System.out.println("Timp până la Crăciun: " + 
    DateTimeUtils.timpRamas(eveniment));

Exemple Avansate și Cazuri Reale

Sistem de Rezervări

public class SistemRezervari {
    private static final LocalTime ORA_DESCHIDERE = LocalTime.of(9, 0);
    private static final LocalTime ORA_INCHIDERE = LocalTime.of(18, 0);
    private static final Duration DURATA_SLOT = Duration.ofMinutes(30);
    
    public static List getSloturiDisponibile(LocalDate data) {
        List sloturi = new ArrayList<>();
        LocalDateTime start = data.atTime(ORA_DESCHIDERE);
        LocalDateTime end = data.atTime(ORA_INCHIDERE);
        
        while (start.isBefore(end)) {
            if (!esteWeekend(start.toLocalDate())) {
                sloturi.add(start);
            }
            start = start.plus(DURATA_SLOT);
        }
        return sloturi;
    }
    
    public static boolean esteSlotValid(LocalDateTime slot) {
        LocalTime ora = slot.toLocalTime();
        return !ora.isBefore(ORA_DESCHIDERE) && 
               !ora.isAfter(ORA_INCHIDERE.minus(DURATA_SLOT)) &&
               !esteWeekend(slot.toLocalDate()) &&
               slot.isAfter(LocalDateTime.now());
    }
    
    private static boolean esteWeekend(LocalDate data) {
        DayOfWeek zi = data.getDayOfWeek();
        return zi == DayOfWeek.SATURDAY || zi == DayOfWeek.SUNDAY;
    }
}

// Utilizare
LocalDate maine = LocalDate.now().plusDays(1);
List sloturi = SistemRezervari.getSloturiDisponibile(maine);
System.out.println("Sloturi disponibile pentru " + maine + ":");
sloturi.forEach(slot -> System.out.println(
    slot.format(DateTimeFormatter.ofPattern("HH:mm"))
));

Calculator Rate și Scadențe

public class CalculatorRate {
    
    public static List genereazaScadente(
            LocalDate dataStart, int numarRate, String frecventa) {
        
        List scadente = new ArrayList<>();
        LocalDate data = dataStart;
        
        for (int i = 0; i < numarRate; i++) {
            switch (frecventa.toLowerCase()) {
                case "lunar":
                    data = data.plusMonths(1);
                    break;
                case "trimestrial":
                    data = data.plusMonths(3);
                    break;
                case "semestrial":
                    data = data.plusMonths(6);
                    break;
                case "anual":
                    data = data.plusYears(1);
                    break;
                case "saptamanal":
                    data = data.plusWeeks(1);
                    break;
            }
            // Ajustare pentru weekend
            if (data.getDayOfWeek() == DayOfWeek.SATURDAY) {
                data = data.plusDays(2);
            } else if (data.getDayOfWeek() == DayOfWeek.SUNDAY) {
                data = data.plusDays(1);
            }
            scadente.add(data);
        }
        return scadente;
    }
    
    public static Map analizeazaScadente(List scadente) {
        Map analiza = new HashMap<>();
        LocalDate azi = LocalDate.now();
        
        long scadenteRestante = scadente.stream()
            .filter(data -> data.isBefore(azi))
            .count();
        
        Optional urmatoareaScadenta = scadente.stream()
            .filter(data -> !data.isBefore(azi))
            .findFirst();
        
        long zileRamase = urmatoareaScadenta
            .map(data -> ChronoUnit.DAYS.between(azi, data))
            .orElse(-1L);
        
        analiza.put("scadenteRestante", scadenteRestante);
        analiza.put("urmatoareaScadenta", urmatoareaScadenta.orElse(null));
        analiza.put("zileRamase", zileRamase);
        analiza.put("totalScadente", scadente.size());
        
        return analiza;
    }
}

// Utilizare
LocalDate start = LocalDate.of(2024, 1, 15);
List scadente = CalculatorRate.genereazaScadente(start, 12, "lunar");
Map analiza = CalculatorRate.analizeazaScadente(scadente);

System.out.println("Analiza scadențe:");
analiza.forEach((key, value) -> System.out.println(key + ": " + value));

Planificator Evenimente Recurente

public class PlanificatorEvenimente {
    
    public enum TipRecurenta {
        ZILNIC, SAPTAMANAL, LUNAR, ANUAL, ZILE_LUCRATOARE
    }
    
    public static List genereazaEvenimente(
            LocalDateTime start, 
            TipRecurenta tip, 
            int numarAparitii,
            Set zileSelectate) {
        
        List evenimente = new ArrayList<>();
        LocalDateTime current = start;
        int contor = 0;
        
        while (contor < numarAparitii) {
            switch (tip) {
                case ZILNIC:
                    evenimente.add(current);
                    current = current.plusDays(1);
                    contor++;
                    break;
                    
                case SAPTAMANAL:
                    if (zileSelectate == null || zileSelectate.contains(current.getDayOfWeek())) {
                        evenimente.add(current);
                        contor++;
                    }
                    current = current.plusDays(1);
                    break;
                    
                case LUNAR:
                    evenimente.add(current);
                    current = current.plusMonths(1);
                    // Ajustare pentru ultima zi din lună
                    if (current.getDayOfMonth() != start.getDayOfMonth()) {
                        current = current.withDayOfMonth(
                            Math.min(start.getDayOfMonth(), 
                                   current.toLocalDate().lengthOfMonth())
                        );
                    }
                    contor++;
                    break;
                    
                case ANUAL:
                    evenimente.add(current);
                    current = current.plusYears(1);
                    contor++;
                    break;
                    
                case ZILE_LUCRATOARE:
                    DayOfWeek zi = current.getDayOfWeek();
                    if (zi != DayOfWeek.SATURDAY && zi != DayOfWeek.SUNDAY) {
                        evenimente.add(current);
                        contor++;
                    }
                    current = current.plusDays(1);
                    break;
            }
        }
        return evenimente;
    }
    
    public static String genereazaDescriere(List evenimente) {
        if (evenimente.isEmpty()) return "Nu există evenimente";
        
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
        StringBuilder sb = new StringBuilder();
        sb.append("Serie de ").append(evenimente.size()).append(" evenimente:\n");
        sb.append("Prima dată: ").append(evenimente.get(0).format(formatter)).append("\n");
        sb.append("Ultima dată: ").append(evenimente.get(evenimente.size()-1).format(formatter)).append("\n");
        
        // Calculare interval mediu
        if (evenimente.size() > 1) {
            Duration totalDuration = Duration.between(
                evenimente.get(0), 
                evenimente.get(evenimente.size()-1)
            );
            long intervalMediu = totalDuration.toDays() / (evenimente.size() - 1);
            sb.append("Interval mediu: ").append(intervalMediu).append(" zile");
        }
        
        return sb.toString();
    }
}

// Exemple de utilizare
LocalDateTime startMeeting = LocalDateTime.of(2024, 3, 15, 10, 0);

// Evenimente zilnice
List zilnic = PlanificatorEvenimente.genereazaEvenimente(
    startMeeting, TipRecurenta.ZILNIC, 7, null
);

// Evenimente săptămânale (luni și joi)
Set zileSelectate = Set.of(DayOfWeek.MONDAY, DayOfWeek.THURSDAY);
List saptamanal = PlanificatorEvenimente.genereazaEvenimente(
    startMeeting, TipRecurenta.SAPTAMANAL, 8, zileSelectate
);

System.out.println(PlanificatorEvenimente.genereazaDescriere(zilnic));
System.out.println(PlanificatorEvenimente.genereazaDescriere(saptamanal));

Analizator Log-uri cu Timestamp

public class AnalizatorLoguri {
    
    public static class EntryLog {
        LocalDateTime timestamp;
        String nivel;
        String mesaj;
        
        public EntryLog(String linie) {
            // Format: 2024-03-15 14:30:45.123 [ERROR] Mesaj eroare
            String[] parti = linie.split(" ", 4);
            this.timestamp = LocalDateTime.parse(
                parti[0] + "T" + parti[1].substring(0, 8)
            );
            this.nivel = parti[2].replace("[", "").replace("]", "");
            this.mesaj = parti[3];
        }
    }
    
    public static Map analizeazaLoguri(List linii) {
        List loguri = linii.stream()
            .map(EntryLog::new)
            .collect(Collectors.toList());
        
        Map rezultat = new HashMap<>();
        
        // Grupare pe nivel
        Map peNivel = loguri.stream()
            .collect(Collectors.groupingBy(
                log -> log.nivel,
                Collectors.counting()
            ));
        
        // Grupare pe oră
        Map peOra = loguri.stream()
            .collect(Collectors.groupingBy(
                log -> log.timestamp.getHour(),
                Collectors.counting()
            ));
        
        // Găsire perioade cu multe erori
        List perioadeProblematice = new ArrayList<>();
        for (int i = 0; i < loguri.size() - 10; i++) {
            List fereastra = loguri.subList(i, i + 10);
            long erori = fereastra.stream()
                .filter(log -> "ERROR".equals(log.nivel))
                .count();
            if (erori > 5) {
                perioadeProblematice.add(fereastra.get(0).timestamp);
            }
        }
        
        // Calculare timp mediu între erori
        List timpuriErori = loguri.stream()
            .filter(log -> "ERROR".equals(log.nivel))
            .map(log -> log.timestamp)
            .sorted()
            .collect(Collectors.toList());
        
        Duration timpMediuIntreErori = null;
        if (timpuriErori.size() > 1) {
            long totalMinute = 0;
            for (int i = 1; i < timpuriErori.size(); i++) {
                totalMinute += Duration.between(
                    timpuriErori.get(i-1), 
                    timpuriErori.get(i)
                ).toMinutes();
            }
            timpMediuIntreErori = Duration.ofMinutes(
                totalMinute / (timpuriErori.size() - 1)
            );
        }
        
        rezultat.put("totalLoguri", loguri.size());
        rezultat.put("distributieNivel", peNivel);
        rezultat.put("distributieOra", peOra);
        rezultat.put("perioadeProblematice", perioadeProblematice);
        rezultat.put("timpMediuIntreErori", timpMediuIntreErori);
        
        return rezultat;
    }
}

// Exemplu de utilizare
List loguri = Arrays.asList(
    "2024-03-15 14:30:45.123 [INFO] Aplicație pornită",
    "2024-03-15 14:31:12.456 [ERROR] Conexiune eșuată",
    "2024-03-15 14:31:15.789 [ERROR] Reîncercare conexiune",
    "2024-03-15 14:32:00.123 [INFO] Conexiune restabilită",
    "2024-03-15 14:35:30.456 [WARNING] Memorie aproape plină"
);

Map analiza = AnalizatorLoguri.analizeazaLoguri(loguri);
analiza.forEach((key, value) -> System.out.println(key + ": " + value));

Manager Timezone-uri

import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public class ManagerTimezone {
    
    public static void afiseazaOraInMultipleTimezone(LocalDateTime ora) {
        List timezones = Arrays.asList(
            "Europe/Bucharest",
            "Europe/London", 
            "America/New_York",
            "Asia/Tokyo",
            "Australia/Sydney"
        );
        
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
            "dd/MM/yyyy HH:mm:ss z"
        );
        
        System.out.println("Ora locală: " + ora);
        System.out.println("-".repeat(50));
        
        for (String tzId : timezones) {
            ZoneId zone = ZoneId.of(tzId);
            ZonedDateTime zdtLocal = ora.atZone(ZoneId.systemDefault());
            ZonedDateTime zdtTarget = zdtLocal.withZoneSameInstant(zone);
            
            System.out.printf("%-20s: %s%n", 
                zone.getId(), 
                zdtTarget.format(formatter)
            );
        }
    }
    
    public static Duration diferentaFusOrar(String timezone1, String timezone2) {
        ZonedDateTime now1 = ZonedDateTime.now(ZoneId.of(timezone1));
        ZonedDateTime now2 = ZonedDateTime.now(ZoneId.of(timezone2));
        
        return Duration.between(
            now1.toLocalDateTime(), 
            now2.toLocalDateTime()
        );
    }
    
    public static LocalDateTime convertesteIntreFusuri(
            LocalDateTime ora, String fusOrigine, String fusDestinatie) {
        
        ZonedDateTime zdtOrigine = ora.atZone(ZoneId.of(fusOrigine));
        ZonedDateTime zdtDestinatie = zdtOrigine.withZoneSameInstant(
            ZoneId.of(fusDestinatie)
        );
        
        return zdtDestinatie.toLocalDateTime();
    }
}

// Utilizare
LocalDateTime meeting = LocalDateTime.of(2024, 3, 15, 14, 0);
ManagerTimezone.afiseazaOraInMultipleTimezone(meeting);

Duration diferenta = ManagerTimezone.diferentaFusOrar(
    "Europe/Bucharest", 
    "America/New_York"
);
System.out.println("\nDiferența de fus: " + diferenta.toHours() + " ore");

LocalDateTime oraNewYork = ManagerTimezone.convertesteIntreFusuri(
    meeting, "Europe/Bucharest", "America/New_York"
);
System.out.println("Ora în New York: " + oraNewYork);

Generator Rapoarte Periodice

public class GeneratorRapoarte {
    
    public static class PerioadaRaport {
        LocalDate inceput;
        LocalDate sfarsit;
        String tip;
        
        public PerioadaRaport(LocalDate inceput, LocalDate sfarsit, String tip) {
            this.inceput = inceput;
            this.sfarsit = sfarsit;
            this.tip = tip;
        }
        
        public String toString() {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
            return String.format("%s: %s - %s", 
                tip, 
                inceput.format(formatter), 
                sfarsit.format(formatter)
            );
        }
    }
    
    public static List genereazaPerioadeRaport(
            int an, String tipRaport) {
        
        List perioade = new ArrayList<>();
        
        switch (tipRaport.toLowerCase()) {
            case "lunar":
                for (int luna = 1; luna <= 12; luna++) {
                    YearMonth ym = YearMonth.of(an, luna);
                    perioade.add(new PerioadaRaport(
                        ym.atDay(1),
                        ym.atEndOfMonth(),
                        ym.getMonth().toString()
                    ));
                }
                break;
                
            case "trimestrial":
                for (int trim = 0; trim < 4; trim++) {
                    LocalDate inceput = LocalDate.of(an, trim * 3 + 1, 1);
                    LocalDate sfarsit = inceput.plusMonths(3).minusDays(1);
                    perioade.add(new PerioadaRaport(
                        inceput,
                        sfarsit,
                        "Trimestrul " + (trim + 1)
                    ));
                }
                break;
                
            case "saptamanal":
                LocalDate start = LocalDate.of(an, 1, 1);
                // Ajustare la luni
                start = start.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
                LocalDate end = LocalDate.of(an, 12, 31);
                
                int saptamana = 1;
                while (start.isBefore(end)) {
                    LocalDate sfarsitSapt = start.plusDays(6);
                    if (sfarsitSapt.isAfter(end)) sfarsitSapt = end;
                    
                    perioade.add(new PerioadaRaport(
                        start,
                        sfarsitSapt,
                        "Săptămâna " + saptamana
                    ));
                    
                    start = start.plusWeeks(1);
                    saptamana++;
                }
                break;
        }
        
        return perioade;
    }
    
    public static Map statisticiPerioada(
            LocalDate inceput, LocalDate sfarsit) {
        
        Map stats = new HashMap<>();
        
        long zileTotale = ChronoUnit.DAYS.between(inceput, sfarsit) + 1;
        long zileLucratoare = inceput.datesUntil(sfarsit.plusDays(1))
            .filter(data -> {
                DayOfWeek zi = data.getDayOfWeek();
                return zi != DayOfWeek.SATURDAY && zi != DayOfWeek.SUNDAY;
            })
            .count();
        
        long weekenduri = zileTotale - zileLucratoare;
        long saptamaniComplete = zileTotale / 7;
        
        stats.put("zileTotale", zileTotale);
        stats.put("zileLucratoare", zileLucratoare);
        stats.put("weekenduri", weekenduri);
        stats.put("saptamaniComplete", saptamaniComplete);
        
        return stats;
    }
}

// Utilizare
List rapoarteTrim = GeneratorRapoarte.genereazaPerioadeRaport(
    2024, "trimestrial"
);
rapoarteTrim.forEach(System.out::println);

System.out.println("\nStatistici Q1 2024:");
Map stats = GeneratorRapoarte.statisticiPerioada(
    LocalDate.of(2024, 1, 1),
    LocalDate.of(2024, 3, 31)
);
stats.forEach((key, value) -> System.out.println(key + ": " + value));

Calculator Business Days cu Sărbători

public class BusinessDayCalculator {
    
    private static final Set SARBATORI_2024 = Set.of(
        LocalDate.of(2024, 1, 1),   // Anul Nou
        LocalDate.of(2024, 1, 2),   // A doua zi
        LocalDate.of(2024, 4, 28),  // Paște Ortodox
        LocalDate.of(2024, 4, 29),  // A doua zi de Paște
        LocalDate.of(2024, 5, 1),   // Ziua Muncii
        LocalDate.of(2024, 6, 16),  // Rusalii
        LocalDate.of(2024, 6, 17),  // A doua zi
        LocalDate.of(2024, 8, 15),  // Adormirea Maicii Domnului
        LocalDate.of(2024, 11, 30), // Sf. Andrei
        LocalDate.of(2024, 12, 1),  // Ziua Națională
        LocalDate.of(2024, 12, 25), // Crăciun
        LocalDate.of(2024, 12, 26)  // A doua zi
    );
    
    public static boolean esteZiLucratoare(LocalDate data) {
        DayOfWeek zi = data.getDayOfWeek();
        return zi != DayOfWeek.SATURDAY && 
               zi != DayOfWeek.SUNDAY && 
               !SARBATORI_2024.contains(data);
    }
    
    public static LocalDate adaugaZileLucratoare(LocalDate start, int zile) {
        LocalDate rezultat = start;
        int zileAdaugate = 0;
        
        while (zileAdaugate < zile) {
            rezultat = rezultat.plusDays(1);
            if (esteZiLucratoare(rezultat)) {
                zileAdaugate++;
            }
        }
        
        return rezultat;
    }
    
    public static long calculeazaZileLucratoare(
            LocalDate start, LocalDate end) {
        
        return start.datesUntil(end.plusDays(1))
            .filter(BusinessDayCalculator::esteZiLucratoare)
            .count();
    }
    
    public static List getUrmatoareleSarbatori(
            LocalDate de la, int numar) {
        
        return SARBATORI_2024.stream()
            .filter(data -> data.isAfter(de la))
            .sorted()
            .limit(numar)
            .collect(Collectors.toList());
    }
    
    public static Map zileLucratoarePeLuna(int an) {
        Map rezultat = new EnumMap<>(Month.class);
        
        for (Month luna : Month.values()) {
            YearMonth ym = YearMonth.of(an, luna);
            LocalDate start = ym.atDay(1);
            LocalDate end = ym.atEndOfMonth();
            
            long zileLucr = calculeazaZileLucratoare(start, end);
            rezultat.put(luna, zileLucr);
        }
        
        return rezultat;
    }
}

// Utilizare
LocalDate azi = LocalDate.now();
LocalDate deadline = BusinessDayCalculator.adaugaZileLucratoare(azi, 10);
System.out.println("Deadline (10 zile lucrătoare): " + deadline);

long zileLucr = BusinessDayCalculator.calculeazaZileLucratoare(
    LocalDate.of(2024, 3, 1),
    LocalDate.of(2024, 3, 31)
);
System.out.println("Zile lucrătoare în martie 2024: " + zileLucr);

System.out.println("\nUrmătoarele sărbători:");
BusinessDayCalculator.getUrmatoareleSarbatori(azi, 3)
    .forEach(System.out::println);

System.out.println("\nZile lucrătoare pe lună în 2024:");
BusinessDayCalculator.zileLucratoarePeLuna(2024)
    .forEach((luna, zile) -> System.out.printf("%s: %d zile%n", luna, zile));