Jalali (Shamsi) converter, so the admin panel can show * Persian dates without pulling in an extra dependency. Only the conversion and a * couple of display formatters are implemented — enough for table columns and infolists. */ class Jalali { private const MONTHS = [ 1 => 'فروردین', 2 => 'اردیبهشت', 3 => 'خرداد', 4 => 'تیر', 5 => 'مرداد', 6 => 'شهریور', 7 => 'مهر', 8 => 'آبان', 9 => 'آذر', 10 => 'دی', 11 => 'بهمن', 12 => 'اسفند', ]; /** * @return array{0: int, 1: int, 2: int} [year, month, day] */ public static function fromGregorian(int $gy, int $gm, int $gd): array { $g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; $gy2 = ($gm > 2) ? ($gy + 1) : $gy; $days = 355666 + (365 * $gy) + intdiv($gy2 + 3, 4) - intdiv($gy2 + 99, 100) + intdiv($gy2 + 399, 400) + $gd + $g_d_m[$gm - 1]; $jy = -1595 + (33 * intdiv($days, 12053)); $days %= 12053; $jy += 4 * intdiv($days, 1461); $days %= 1461; if ($days > 365) { $jy += intdiv($days - 1, 365); $days = ($days - 1) % 365; } if ($days < 186) { $jm = 1 + intdiv($days, 31); $jd = 1 + ($days % 31); } else { $jm = 7 + intdiv($days - 186, 30); $jd = 1 + (($days - 186) % 30); } return [$jy, $jm, $jd]; } public static function date(DateTimeInterface|CarbonInterface|string|null $value): ?string { $date = self::normalize($value); if ($date === null) { return null; } [$jy, $jm, $jd] = self::fromGregorian((int) $date->format('Y'), (int) $date->format('n'), (int) $date->format('j')); return self::toPersianDigits(sprintf('%04d/%02d/%02d', $jy, $jm, $jd)); } public static function dateTime(DateTimeInterface|CarbonInterface|string|null $value): ?string { $date = self::normalize($value); if ($date === null) { return null; } [$jy, $jm, $jd] = self::fromGregorian((int) $date->format('Y'), (int) $date->format('n'), (int) $date->format('j')); return self::toPersianDigits(sprintf( '%04d/%02d/%02d - %02d:%02d', $jy, $jm, $jd, (int) $date->format('H'), (int) $date->format('i') )); } public static function longDate(DateTimeInterface|CarbonInterface|string|null $value): ?string { $date = self::normalize($value); if ($date === null) { return null; } [$jy, $jm, $jd] = self::fromGregorian((int) $date->format('Y'), (int) $date->format('n'), (int) $date->format('j')); return self::toPersianDigits($jd.' '.self::MONTHS[$jm].' '.$jy); } private static function normalize(DateTimeInterface|CarbonInterface|string|null $value): ?DateTimeInterface { if ($value === null || $value === '') { return null; } if ($value instanceof DateTimeInterface) { return $value; } try { return new \DateTimeImmutable((string) $value); } catch (\Throwable) { return null; } } private static function toPersianDigits(string $value): string { return strtr($value, [ '0' => '۰', '1' => '۱', '2' => '۲', '3' => '۳', '4' => '۴', '5' => '۵', '6' => '۶', '7' => '۷', '8' => '۸', '9' => '۹', ]); } }