*/ private const MONTHS = [ 1 => 'Enero', 2 => 'Febrero', 3 => 'Marzo', 4 => 'Abril', 5 => 'Mayo', 6 => 'Junio', 7 => 'Julio', 8 => 'Agosto', 9 => 'Septiembre', 10 => 'Octubre', 11 => 'Noviembre', 12 => 'Diciembre', ]; /** @param iterable $dates */ public function format(iterable $dates): ?string { $normalizedDates = collect($dates) ->map(fn (string $date): DateTimeImmutable => new DateTimeImmutable($date)) ->unique(fn (DateTimeImmutable $date): string => $date->format('Y-m-d')) ->sortBy(fn (DateTimeImmutable $date): string => $date->format('Y-m-d')) ->values(); if ($normalizedDates->isEmpty()) { return null; } $years = $normalizedDates ->groupBy(fn (DateTimeImmutable $date): string => $date->format('Y')) ->map(function ($yearDates, string $year): string { $months = $yearDates ->groupBy(fn (DateTimeImmutable $date): string => $date->format('n')) ->map(function ($monthDates, string $month): string { $days = $monthDates ->map(fn (DateTimeImmutable $date): string => (string) ((int) $date->format('j'))) ->values() ->all(); return $this->join($days).' de '.self::MONTHS[(int) $month]; }) ->values() ->all(); return $this->join($months).' '.$year; }) ->values() ->all(); return $this->join($years); } /** @param array $parts */ private function join(array $parts): string { if (count($parts) <= 1) { return $parts[0] ?? ''; } $last = array_pop($parts); return implode(', ', $parts).' y '.$last; } }