Passed
Pull Request — master (#11)
by Alex
02:52
created

DateTimeFactory::createDateTimeZone()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 10
c 1
b 1
f 0
dl 0
loc 13
rs 9.9332
cc 2
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arp\DateTime;
6
7
use Arp\DateTime\Exception\DateTimeFactoryException;
8
use Arp\DateTime\Exception\DateTimeZoneFactoryException;
9
10
/**
11
 * @author  Alex Patterson <[email protected]>
12
 * @package Arp\DateTime
13
 */
14
final class DateTimeFactory implements DateTimeFactoryInterface
15
{
16
    /**
17
     * @var DateTimeZoneFactoryInterface
18
     */
19
    private DateTimeZoneFactoryInterface $dateTimeZoneFactory;
20
21
    /**
22
     * @var string
23
     */
24
    private string $dateTimeClassName;
25
26
    /**
27
     * @param DateTimeZoneFactoryInterface|null $dateTimeZoneFactory
28
     * @param string|null                       $dateTimeClassName
29
     *
30
     * @throws DateTimeFactoryException
31
     */
32
    public function __construct(
33
        DateTimeZoneFactoryInterface $dateTimeZoneFactory = null,
34
        string $dateTimeClassName = null
35
    ) {
36
        $this->dateTimeZoneFactory = $dateTimeZoneFactory ?? new DateTimeZoneFactory();
37
38
        $dateTimeClassName ??= \DateTime::class;
39
        if (!is_a($dateTimeClassName, \DateTimeInterface::class, true)) {
40
            throw new DateTimeFactoryException(
41
                sprintf(
42
                    'The \'dateTimeClassName\' parameter must be a class name that implements \'%s\'',
43
                    \DateTimeInterface::class
44
                )
45
            );
46
        }
47
48
        $this->dateTimeClassName = $dateTimeClassName;
49
    }
50
51
    /**
52
     * @param null|string               $spec     The date and time specification
53
     * @param string|\DateTimeZone|null $timeZone The date time zone; if omitted or null the PHP default will be used
54
     *
55
     * @return \DateTimeInterface
56
     *
57
     * @throws DateTimeFactoryException If the \DateTime instance cannot be created.
58
     */
59
    public function createDateTime(?string $spec = null, $timeZone = null): \DateTimeInterface
60
    {
61
        $dateTimeZone = $this->resolveDateTimeZone($timeZone);
62
63
        try {
64
            /** @var \DateTimeInterface $dateTime */
65
            /** @noinspection PhpUnnecessaryLocalVariableInspection */
66
            /** @noinspection OneTimeUseVariablesInspection */
67
            $dateTime = new $this->dateTimeClassName($spec ?? 'now', $dateTimeZone);
68
            return $dateTime;
69
        } catch (\Exception $e) {
70
            throw new DateTimeFactoryException(
71
                sprintf(
72
                    'Failed to create a valid \DateTime instance using \'%s\': %s',
73
                    $spec,
74
                    $e->getMessage()
75
                ),
76
                $e->getCode(),
77
                $e
78
            );
79
        }
80
    }
81
82
    /**
83
     * @param string                    $format   The date and time format
84
     * @param string                    $spec     The date and time specification
85
     * @param string|\DateTimeZone|null $timeZone The date time zone; if omitted or null the PHP default will be used
86
     *
87
     * @return \DateTimeInterface
88
     *
89
     * @throws DateTimeFactoryException  If the \DateTime instance cannot be created
90
     */
91
    public function createFromFormat(string $format, string $spec, $timeZone = null): \DateTimeInterface
92
    {
93
        /** @var callable $factory */
94
        $factory = [$this->dateTimeClassName, 'createFromFormat'];
95
96
        $dateTime = $factory($format, $spec, $this->resolveDateTimeZone($timeZone));
97
98
        if (!$dateTime instanceof \DateTimeInterface) {
99
            throw new DateTimeFactoryException(
100
                sprintf(
101
                    'Failed to create a valid \DateTime instance using \'%s\' and format \'%s\'',
102
                    $spec,
103
                    $format
104
                )
105
            );
106
        }
107
108
        return $dateTime;
109
    }
110
111
    /**
112
     * @param mixed|string|\DateTimeZone|null $timeZone
113
     *
114
     * @return \DateTimeZone|null
115
     *
116
     * @throws DateTimeFactoryException
117
     */
118
    private function resolveDateTimeZone($timeZone): ?\DateTimeZone
119
    {
120
        if (empty($timeZone) || (!is_string($timeZone) && !$timeZone instanceof \DateTimeZone)) {
121
            return null;
122
        }
123
124
        try {
125
            return is_string($timeZone)
126
                ? $this->dateTimeZoneFactory->createDateTimeZone($timeZone)
127
                : $timeZone;
128
        } catch (DateTimeZoneFactoryException $e) {
129
            throw new DateTimeFactoryException(
130
                sprintf('Failed to create date time zone: %s', $e->getMessage()),
131
                $e->getCode(),
132
                $e
133
            );
134
        }
135
    }
136
}
137