$now->between($from, $to), $from !== null => $now->greaterThanOrEqualTo($from), $to !== null => $now->lessThanOrEqualTo($to), default => false, }; } public static function toRfc3339(\DateTimeInterface | string $datetime): string { $dt = is_string($datetime) ? self::parseDateTime($datetime) : $datetime; if ($dt instanceof \DateTimeImmutable) { $dt = $dt->setTimezone(new \DateTimeZone('UTC')); } elseif ($dt instanceof \DateTime) { $dt = (clone $dt)->setTimezone(new \DateTimeZone('UTC')); } return $dt->format(\DATE_RFC3339); } public static function toNullableRfc3339(\DateTimeInterface | string | null $datetime): ?string { return $datetime !== null ? self::toRfc3339($datetime) : null; } public static function parseDateTime(string $datetime): \DateTimeInterface { $datetime = trim($datetime); /** @var \DateTimeInterface|null */ $output = null; switch (true) { case (bool) preg_match(self::FRENCH_DATE_REGEX, $datetime): $output = self::parseFrench($datetime); break; case (bool) preg_match(self::FRENCH_DATE_INVERT_REGEX, $datetime): $output = self::parseFrenchInverted($datetime); break; case (bool) preg_match(self::ISO8601_REGEX, $datetime): $output = self::parseIso8601($datetime); break; case (bool) preg_match(self::RFC3339_REGEX, $datetime): $output = self::parseRfc3339($datetime); break; default: try { $output = (new \DateTimeImmutable($datetime, new \DateTimeZone('UTC')))->setTimezone(new \DateTimeZone('UTC')); } catch (\Exception) { throw InvalidDateFormatException::forDateString($datetime); } } return $output; } protected static function parseFrench(string $datetime): \DateTimeInterface { $dt = \DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $datetime . ' 00:00:00', new \DateTimeZone('UTC')); return $dt ?: self::parseFallback($datetime); } protected static function parseFrenchInverted(string $datetime): \DateTimeInterface { $dt = \DateTimeImmutable::createFromFormat('d-m-Y H:i:s', $datetime . ' 00:00:00', new \DateTimeZone('UTC')); return $dt ?: self::parseFallback($datetime); } protected static function parseRfc3339(string $datetime): \DateTimeInterface { $dt = \DateTimeImmutable::createFromFormat(\DateTimeInterface::RFC3339, $datetime); if ($dt !== false) { return $dt->setTimezone(new \DateTimeZone('UTC')); } $dt = \DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.uP', $datetime); return $dt ? $dt->setTimezone(new \DateTimeZone('UTC')) : self::parseFallback($datetime); } protected static function parseIso8601(string $datetime): \DateTimeInterface { $dt = \DateTimeImmutable::createFromFormat(\DateTimeInterface::ATOM, $datetime); return $dt ? $dt->setTimezone(new \DateTimeZone('UTC')) : self::parseFallback($datetime); } protected static function parseFallback(string $datetime): \DateTimeInterface { try { return (new \DateTimeImmutable($datetime, new \DateTimeZone('UTC')))->setTimezone(new \DateTimeZone('UTC')); } catch (\Throwable) { throw InvalidDateFormatException::forDateString($datetime); } } }