Completed
Pull Request — master (#75)
by ignace nyamagana
04:36
created

Duration::__construct()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 1
dl 0
loc 15
ccs 10
cts 10
cp 1
crap 3
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * League.Period (https://period.thephpleague.com)
5
 *
6
 * (c) Ignace Nyamagana Butera <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace League\Period;
15
16
use DateInterval;
17
use TypeError;
18
use function filter_var;
19
use function gettype;
20
use function is_string;
21
use function method_exists;
22
use function preg_match;
23
use function property_exists;
24
use function rtrim;
25
use function sprintf;
26
use function str_pad;
27
use const FILTER_VALIDATE_INT;
28
29
/**
30
 * League Period Duration.
31
 *
32
 * @package League.period
33
 * @author  Ignace Nyamagana Butera <[email protected]>
34
 * @since   4.2.0
35
 */
36
final class Duration extends DateInterval
37
{
38
    private const REGEXP_MICROSECONDS_INTERVAL_SPEC = '@^(?<interval>.*)(\.|,)(?<fraction>\d{1,6})S$@';
39
40
    private const REGEXP_MICROSECONDS_DATE_SPEC = '@^(?<interval>.*)(\.)(?<fraction>\d{1,6})$@';
41
42
    private const REGEXP_CHRONO_FORMAT = '@^
43
        (
44
            ((?<hour>\d+):)?
45
            (?<minute>\d+):
46
        )?
47
        (
48
            (?<second>\d+)
49
            (\.(?<fraction>\d{1,6}))?
50
        )
51
    $@x';
52
53
    /**
54
     * Returns a continuous portion of time between two datepoints expressed as a DateInterval object.
55
     *
56
     * The duration can be
57
     * <ul>
58
     * <li>an Period object</li>
59
     * <li>a DateInterval object</li>
60
     * <li>an integer interpreted as the duration expressed in seconds.</li>
61
     * <li>a string parsable by DateInterval::createFromDateString</li>
62
     * </ul>
63
     *
64
     * @param mixed $duration a continuous portion of time
65
     */
66 240
    public static function create($duration): self
67
    {
68 240
        if ($duration instanceof Period) {
69 12
            $duration = $duration->getDateInterval();
70
        }
71
72 240
        if ($duration instanceof DateInterval) {
73 21
            $new = new self('PT0S');
74 21
            foreach ($duration as $name => $value) {
75 21
                if (property_exists($new, $name)) {
76 21
                    $new->$name = $value;
77
                }
78
            }
79
80 21
            return $new;
81
        }
82
83 219
        if (false !== ($second = filter_var($duration, FILTER_VALIDATE_INT))) {
84 60
            return new self('PT'.$second.'S');
85
        }
86
87 159
        if (!is_string($duration) && !method_exists($duration, '__toString')) {
88 9
            throw new TypeError(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, gettype($duration)));
89
        }
90
91 150
        $duration = (string) $duration;
92 150
        if (1 === preg_match(self::REGEXP_CHRONO_FORMAT, $duration, $matches)) {
93 12
            $matches['hour'] = $matches['hour'] ?? '0';
94 12
            $matches['minute'] = $matches['minute'] ?? '0';
95 12
            $matches['fraction'] = str_pad($matches['fraction'] ?? '0000000', 6, '0');
96
97 12
            return self::createFromDateString(
98 12
                $matches['hour'].' hours '.
99 12
                $matches['minute'].' minutes '.
100 12
                $matches['second'].' seconds '.
101 12
                $matches['fraction'].' microseconds'
102
            );
103
        }
104
105 138
        return self::createFromDateString($duration);
106
    }
107
108
    /**
109
     * @inheritdoc
110
     *
111
     * @param mixed $duration a date with relative parts
112
     */
113 153
    public static function createFromDateString($duration): self
114
    {
115 153
        $duration = parent::createFromDateString($duration);
116 153
        $new = new self('PT0S');
117 153
        foreach ($duration as $name => $value) {
118 153
            $new->$name = $value;
119
        }
120
121 153
        return $new;
122
    }
123
124
    /**
125
     * New instance.
126
     *
127
     * Returns a new instance from an Interval specification
128
     */
129 237
    public function __construct(string $interval_spec)
130
    {
131 237
        if (1 === preg_match(self::REGEXP_MICROSECONDS_INTERVAL_SPEC, $interval_spec, $matches)) {
132 3
            parent::__construct($matches['interval'].'S');
133 3
            $this->f = (float) str_pad($matches['fraction'], 6, '0') / 1e6;
134 3
            return;
135
        }
136
137 237
        if (1 === preg_match(self::REGEXP_MICROSECONDS_DATE_SPEC, $interval_spec, $matches)) {
138 3
            parent::__construct($matches['interval']);
139 3
            $this->f = (float) str_pad($matches['fraction'], 6, '0') / 1e6;
140 3
            return;
141
        }
142
143 234
        parent::__construct($interval_spec);
144 228
    }
145
146
    /**
147
     * Returns the ISO8601 interval string representation.
148
     *
149
     * Microseconds fractions are included
150
     */
151 42
    public function __toString(): string
152
    {
153 42
        $date = 'P';
154 42
        foreach (['Y' => $this->y, 'M' => $this->m, 'D' => $this->d] as $key => $value) {
155 42
            if (0 !== $value) {
156 31
                $date .= $value.$key;
157
            }
158
        }
159
160 42
        $time = 'T';
161 42
        foreach (['H' => $this->h, 'M' => $this->i] as $key => $value) {
162 42
            if (0 !== $value) {
163 33
                $time .= $value.$key;
164
            }
165
        }
166
167 42
        if (0.0 !== $this->f) {
0 ignored issues
show
introduced by
The condition 0.0 !== $this->f is always true.
Loading history...
168 12
            $time .= rtrim(sprintf('%f', $this->s + $this->f), '0').'S';
169
170 12
            return $date.$time;
171
        }
172
173 30
        if (0 !== $this->s) {
174 15
            $time .= $this->s.'S';
175
176 15
            return $date.$time;
177
        }
178
179 15
        if ('T' !== $time) {
180 3
            return $date.$time;
181
        }
182
183 12
        if ('P' !== $date) {
184 9
            return $date;
185
        }
186
187 3
        return 'PT0S';
188
    }
189
}
190