*/ 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 { return $this->formatWithOptions($dates, false, false); } /** @param iterable $dates */ public function formatForSentence(iterable $dates): ?string { return $this->formatWithOptions($dates, true, true); } /** @param iterable $dates */ private function formatWithOptions( iterable $dates, bool $padDays, bool $includeYearPreposition, ): ?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) use ($padDays, $includeYearPreposition): string { $months = $yearDates ->groupBy(fn (DateTimeImmutable $date): string => $date->format('n')) ->map(function ($monthDates, string $month) use ($padDays): string { $days = $monthDates ->map(fn (DateTimeImmutable $date): string => $padDays ? $date->format('d') : (string) ((int) $date->format('j'))) ->values() ->all(); return $this->join($days).' de '.self::MONTHS[(int) $month]; }) ->values() ->all(); return $this->join($months).($includeYearPreposition ? ' de ' : ' ').$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; } }