Passed
Pull Request — master (#7)
by Alex
12:05
created

DateTimeFactory::diff()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
c 0
b 0
f 0
dl 0
loc 9
rs 10
cc 2
nc 2
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arp\DateTime;
6
7
use Arp\DateTime\Exception\DateIntervalFactoryException;
8
use Arp\DateTime\Exception\DateTimeFactoryException;
9
10
/**
11
 * @author  Alex Patterson <[email protected]>
12
 * @package Arp\DateTime
13
 */
14
final class DateTimeFactory implements DateTimeFactoryInterface, DateIntervalFactoryInterface
15
{
16
    /**
17
     * @var DateIntervalFactoryInterface
18
     */
19
    private DateIntervalFactoryInterface $dateIntervalFactory;
20
21
    /**
22
     * @param DateIntervalFactoryInterface|null $dateIntervalFactory
23
     */
24
    public function __construct(DateIntervalFactoryInterface $dateIntervalFactory = null)
25
    {
26
        $this->dateIntervalFactory = $dateIntervalFactory ?? new DateIntervalFactory();
27
    }
28
29
    /**
30
     * @param null|string               $spec     The date and time specification.
31
     * @param string|\DateTimeZone|null $timeZone The date time zone. If omitted or null the PHP default will be used.
32
     *
33
     * @return \DateTimeInterface
34
     *
35
     * @throws DateTimeFactoryException If the \DateTime instance cannot be created.
36
     */
37
    public function createDateTime(string $spec = null, $timeZone = null): \DateTimeInterface
38
    {
39
        try {
40
            return new \DateTime($spec ?? 'now', $this->resolveDateTimeZone($timeZone));
41
        } catch (\Throwable $e) {
42
            throw new DateTimeFactoryException(
43
                sprintf(
44
                    'Failed to create a valid \DateTime instance using \'%s\': %s',
45
                    $spec,
46
                    $e->getMessage()
47
                ),
48
                $e->getCode(),
49
                $e
50
            );
51
        }
52
    }
53
54
    /**
55
     * Create a new \DateTime instance using the provided format.
56
     *
57
     * @param string                    $spec     The date and time specification.
58
     * @param string                    $format   The date and time format.
59
     * @param string|\DateTimeZone|null $timeZone The date time zone. If omitted or null the PHP default will be used.
60
     *
61
     * @return \DateTimeInterface
62
     *
63
     * @throws DateTimeFactoryException  If the \DateTime instance cannot be created.
64
     */
65
    public function createFromFormat(string $spec, string $format, $timeZone = null): \DateTimeInterface
66
    {
67
        $dateTime = \DateTime::createFromFormat($format, $spec, $this->resolveDateTimeZone($timeZone));
68
69
        if (false === $dateTime || !$dateTime instanceof \DateTimeInterface) {
70
            throw new DateTimeFactoryException(
71
                sprintf(
72
                    'Failed to create a valid \DateTime instance using \'%s\' and format \'%s\'',
73
                    $spec,
74
                    $format
75
                )
76
            );
77
        }
78
79
        return $dateTime;
80
    }
81
82
    /**
83
     * Create a new \DateTimeZone instance using the provided specification.
84
     *
85
     * @param string $spec The date time zone specification.
86
     *
87
     * @return \DateTimeZone
88
     *
89
     * @throws DateTimeFactoryException If the \DateTimeZone cannot be created.
90
     */
91
    public function createDateTimeZone(string $spec): \DateTimeZone
92
    {
93
        try {
94
            return new \DateTimeZone($spec);
95
        } catch (\Throwable $e) {
96
            throw new DateTimeFactoryException(
97
                sprintf(
98
                    'Failed to create a valid \DateTimeZone instance using \'%s\': %s',
99
                    $spec,
100
                    $e->getMessage()
101
                ),
102
                $e->getCode(),
103
                $e
104
            );
105
        }
106
    }
107
108
    /**
109
     * @param string $spec
110
     *
111
     * @return \DateInterval
112
     *
113
     * @throws DateTimeFactoryException
114
     */
115
    public function createDateInterval(string $spec): \DateInterval
116
    {
117
        try {
118
            return $this->dateIntervalFactory->createDateInterval($spec);
119
        } catch (DateIntervalFactoryException $e) {
120
            throw new DateTimeFactoryException(
121
                sprintf('Failed to create date interval \'%s\': %s', $spec, $e->getMessage()),
122
                $e->getCode(),
123
                $e
124
            );
125
        }
126
    }
127
128
    /**
129
     * Perform a diff of two dates and return the \DateInterval
130
     *
131
     * @param \DateTimeInterface $origin    The origin date
132
     * @param \DateTimeInterface $target    The date to compare to
133
     * @param bool               $absolute  If the interval is negative, should it be forced to be a positive value?
134
     *
135
     * @return \DateInterval
136
     *
137
     * @throws DateTimeFactoryException If the date diff cannot be performed
138
     */
139
    public function diff(\DateTimeInterface $origin, \DateTimeInterface $target, bool $absolute = false): \DateInterval
140
    {
141
        try {
142
            return $this->dateIntervalFactory->diff($origin, $target, $absolute);
143
        } catch (DateIntervalFactoryException $e) {
144
            throw new DateTimeFactoryException(
145
                sprintf('Failed to perform date diff: %s', $e->getMessage()),
146
                $e->getCode(),
147
                $e
148
            );
149
        }
150
    }
151
152
    /**
153
     * Resolve a date time zone from the provided $timeZone argument.
154
     *
155
     * @param string|null|\DateTimeZone $timeZone
156
     *
157
     * @return \DateTimeZone|null
158
     *
159
     * @throws DateTimeFactoryException
160
     */
161
    private function resolveDateTimeZone($timeZone): ?\DateTimeZone
162
    {
163
        if (null === $timeZone || empty($timeZone)) {
164
            return null;
165
        }
166
167
        if (is_string($timeZone)) {
168
            $timeZone = $this->createDateTimeZone($timeZone);
169
        }
170
171
        if (!$timeZone instanceof \DateTimeZone) {
0 ignored issues
show
introduced by
$timeZone is always a sub-type of DateTimeZone.
Loading history...
172
            throw new DateTimeFactoryException(
173
                sprintf(
174
                    'The \'timeZone\' argument must be a \'string\''
175
                    . 'or an object of type \'%s\'; \'%s\' provided in \'%s\'',
176
                    \DateTimeZone::class,
177
                    is_object($timeZone) ? get_class($timeZone) : gettype($timeZone),
178
                    __FUNCTION__
179
                )
180
            );
181
        }
182
183
        return $timeZone;
184
    }
185
}
186