1: <?php declare(strict_types=1);
2:
3: namespace Salient\Core\Date;
4:
5: use Salient\Contract\Core\DateFormatterInterface;
6: use Salient\Contract\Core\DateParserInterface;
7: use Salient\Utility\Date;
8: use DateTime;
9: use DateTimeImmutable;
10: use DateTimeInterface;
11: use DateTimeZone;
12:
13: /**
14: * @api
15: */
16: final class DateFormatter implements DateFormatterInterface
17: {
18: private string $Format;
19: private ?DateTimeZone $Timezone;
20: /** @var non-empty-array<DateParserInterface> */
21: private array $Parsers;
22: private string $TimezoneName;
23:
24: /**
25: * @api
26: *
27: * @param string $format Passed to {@see DateTimeInterface::format()}. See
28: * {@link https://www.php.net/manual/en/datetime.format.php} for syntax.
29: * @param DateTimeZone|string|null $timezone Applied before formatting.
30: * {@see DateTime} objects are not modified.
31: * @param DateParserInterface ...$parsers {@see parse()} tries each of the
32: * given parsers in turn and returns the first result that is not `null`.
33: *
34: * If no parsers are given, a {@see DateFormatParser}, created with the same
35: * `$format`, is used.
36: */
37: public function __construct(
38: string $format = DateTimeInterface::ATOM,
39: $timezone = null,
40: DateParserInterface ...$parsers
41: ) {
42: $this->Format = $format;
43: $this->Timezone = is_string($timezone)
44: ? new DateTimeZone($timezone)
45: : $timezone;
46: $this->Parsers = $parsers
47: ? $parsers
48: : [new DateFormatParser($format)];
49:
50: if ($this->Timezone) {
51: $this->TimezoneName = $this->Timezone->getName();
52: }
53: }
54:
55: /**
56: * @inheritDoc
57: */
58: public function format(DateTimeInterface $date): string
59: {
60: if (
61: $this->Timezone
62: && $this->TimezoneName !== $date->getTimezone()->getName()
63: ) {
64: $date = Date::immutable($date)->setTimezone($this->Timezone);
65: }
66: return $date->format($this->Format);
67: }
68:
69: /**
70: * @inheritDoc
71: */
72: public function parse(string $value, ?DateTimeZone $timezone = null): ?DateTimeImmutable
73: {
74: $timezone ??= $this->Timezone;
75: foreach ($this->Parsers as $parser) {
76: if ($date = $parser->parse($value, $timezone)) {
77: return $date;
78: }
79: }
80: return null;
81: }
82: }
83: