Passed
Pull Request — master (#7)
by Alex
07:24
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
9
/**
10
 * @author  Alex Patterson <[email protected]>
11
 * @package Arp\DateTime
12
 */
13
final class DateTimeFactory implements DateTimeFactoryInterface
14
{
15
    /**
16
     * @var string
17
     */
18
    private string $dateTimeClassName;
19
20
    /**
21
     * @var string
22
     */
23
    private string $dateTimeZoneClassName;
24
25
    /**
26
     * @param string|null $dateTimeClassName
27
     * @param string|null $dateTimeZoneClassName
28
     *
29
     * @throws DateTimeFactoryException
30
     */
31
    public function __construct(string $dateTimeClassName = null, string $dateTimeZoneClassName = null)
32
    {
33
        $dateTimeClassName ??= \DateTime::class;
34
        if (!is_a($dateTimeClassName, \DateTimeInterface::class, true)) {
35
            throw new DateTimeFactoryException(
36
                sprintf(
37
                    'The \'dateTimeClassName\' must the fully qualified class name'
38
                    . ' of a class that implements \'%s\'; \'%s\' provided',
39
                    \DateTimeInterface::class,
40
                    $dateTimeClassName
41
                )
42
            );
43
        }
44
45
        $dateTimeZoneClassName ??= \DateTimeZone::class;
46
        if (!is_a($dateTimeZoneClassName, \DateTimeZone::class, true)) {
47
            throw new DateTimeFactoryException(
48
                sprintf(
49
                    'The \'dateTimeZoneClassName\' must the fully qualified class name'
50
                    . ' of a class that implements \'%s\'; \'%s\' provided',
51
                    \DateTimeZone::class,
52
                    $dateTimeZoneClassName
53
                )
54
            );
55
        }
56
57
        $this->dateTimeClassName = $dateTimeClassName;
58
        $this->dateTimeZoneClassName = $dateTimeZoneClassName;
59
    }
60
61
    /**
62
     * @param null|string               $spec     The date and time specification
63
     * @param string|\DateTimeZone|null $timeZone The date time zone; if omitted or null the PHP default will be used
64
     *
65
     * @return \DateTimeInterface
66
     *
67
     * @throws DateTimeFactoryException If the \DateTime instance cannot be created.
68
     */
69
    public function createDateTime(string $spec = null, $timeZone = null): \DateTimeInterface
70
    {
71
        try {
72
            return (new $this->dateTimeClassName($spec ?? 'now', $this->resolveDateTimeZone($timeZone)));
73
        } catch (\Throwable $e) {
74
            throw new DateTimeFactoryException(
75
                sprintf(
76
                    'Failed to create a valid \DateTime instance using \'%s\': %s',
77
                    $spec,
78
                    $e->getMessage()
79
                ),
80
                $e->getCode(),
81
                $e
82
            );
83
        }
84
    }
85
86
    /**
87
     * @param string                    $spec     The date and time specification
88
     * @param string                    $format   The date and time format
89
     * @param string|\DateTimeZone|null $timeZone The date time zone; if omitted or null the PHP default will be used
90
     *
91
     * @return \DateTimeInterface
92
     *
93
     * @throws DateTimeFactoryException  If the \DateTime instance cannot be created.
94
     */
95
    public function createFromFormat(string $spec, string $format, $timeZone = null): \DateTimeInterface
96
    {
97
        $dateTime = call_user_func(
98
            [$this->dateTimeClassName, 'createFromFormat'],
99
            $format,
100
            $spec,
101
            $this->resolveDateTimeZone($timeZone)
102
        );
103
104
        if (false === $dateTime || !$dateTime instanceof \DateTimeInterface) {
105
            throw new DateTimeFactoryException(
106
                sprintf(
107
                    'Failed to create a valid \DateTime instance using \'%s\' and format \'%s\'',
108
                    $spec,
109
                    $format
110
                )
111
            );
112
        }
113
114
        return $dateTime;
115
    }
116
117
    /**
118
     * @param string $spec The date time zone specification
119
     *
120
     * @return \DateTimeZone
121
     *
122
     * @throws DateTimeFactoryException If the \DateTimeZone cannot be created
123
     */
124
    public function createDateTimeZone(string $spec): \DateTimeZone
125
    {
126
        try {
127
            return (new $this->dateTimeZoneClassName($spec));
128
        } catch (\Throwable $e) {
129
            throw new DateTimeFactoryException(
130
                sprintf(
131
                    'Failed to create a valid \DateTimeZone instance using \'%s\': %s',
132
                    $spec,
133
                    $e->getMessage()
134
                ),
135
                $e->getCode(),
136
                $e
137
            );
138
        }
139
    }
140
141
    /**
142
     * @param string|null|\DateTimeZone $timeZone
143
     *
144
     * @return \DateTimeZone|null|string
145
     *
146
     * @throws DateTimeFactoryException
147
     */
148
    private function resolveDateTimeZone($timeZone): ?\DateTimeZone
149
    {
150
        if (null === $timeZone || empty($timeZone)) {
151
            return null;
152
        }
153
154
        if (is_string($timeZone)) {
155
            $timeZone = $this->createDateTimeZone($timeZone);
156
        }
157
158
        if (!$timeZone instanceof \DateTimeZone) {
0 ignored issues
show
introduced by
$timeZone is always a sub-type of DateTimeZone.
Loading history...
159
            throw new DateTimeFactoryException(
160
                sprintf(
161
                    'The \'timeZone\' argument must be a \'string\''
162
                    . 'or an object of type \'%s\'; \'%s\' provided in \'%s\'',
163
                    \DateTimeZone::class,
164
                    is_object($timeZone) ? get_class($timeZone) : gettype($timeZone),
165
                    __FUNCTION__
166
                )
167
            );
168
        }
169
170
        return $timeZone;
171
    }
172
}
173