Completed
Push — master ( 992c2c...4571ba )
by ignace nyamagana
03:39
created

Duration::__toString()   B

Complexity

Conditions 10
Paths 54

Size

Total Lines 42
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 10.0082

Importance

Changes 0
Metric Value
cc 10
eloc 22
nc 54
nop 0
dl 0
loc 42
ccs 22
cts 23
cp 0.9565
crap 10.0082
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 DateTimeImmutable;
18
use TypeError;
19
use function filter_var;
20
use function gettype;
21
use function is_string;
22
use function method_exists;
23
use function preg_match;
24
use function property_exists;
25
use function rtrim;
26
use function sprintf;
27
use function str_pad;
28
use const FILTER_VALIDATE_INT;
29
30
/**
31
 * League Period Duration.
32
 *
33
 * @package League.period
34
 * @author  Ignace Nyamagana Butera <[email protected]>
35
 * @since   4.2.0
36
 */
37
final class Duration extends DateInterval
38
{
39
    private const REGEXP_MICROSECONDS_INTERVAL_SPEC = '@^(?<interval>.*)(\.|,)(?<fraction>\d{1,6})S$@';
40
41
    private const REGEXP_MICROSECONDS_DATE_SPEC = '@^(?<interval>.*)(\.)(?<fraction>\d{1,6})$@';
42
43
    private const REGEXP_CHRONO_FORMAT = '@^
44
        (?<sign>\+|-)?
45
        (((?<hour>\d+):)?(?<minute>\d+):)?
46
        ((?<second>\d+)(\.(?<fraction>\d{1,6}))?)
47
    $@x';
48
49
    /**
50
     * New instance.
51
     *
52
     * Returns a new instance from an Interval specification
53
     */
54 258
    public function __construct(string $interval_spec)
55
    {
56 258
        if (1 === preg_match(self::REGEXP_MICROSECONDS_INTERVAL_SPEC, $interval_spec, $matches)) {
57 3
            parent::__construct($matches['interval'].'S');
58 3
            $this->f = (float) str_pad($matches['fraction'], 6, '0') / 1e6;
59 3
            return;
60
        }
61
62 258
        if (1 === preg_match(self::REGEXP_MICROSECONDS_DATE_SPEC, $interval_spec, $matches)) {
63 3
            parent::__construct($matches['interval']);
64 3
            $this->f = (float) str_pad($matches['fraction'], 6, '0') / 1e6;
65 3
            return;
66
        }
67
68 255
        parent::__construct($interval_spec);
69 249
    }
70
71
    /**
72
     * Returns a continuous portion of time between two datepoints expressed as a DateInterval object.
73
     *
74
     * The duration can be
75
     * <ul>
76
     * <li>an Period object</li>
77
     * <li>a DateInterval object</li>
78
     * <li>an integer interpreted as the duration expressed in seconds.</li>
79
     * <li>a string parsable by DateInterval::createFromDateString</li>
80
     * </ul>
81
     *
82
     * @param mixed $duration a continuous portion of time
83
     *
84
     * @throws TypeError if the duration type is not a supported
85
     */
86 261
    public static function create($duration): self
87
    {
88 261
        if ($duration instanceof Period) {
89 12
            $duration = $duration->getDateInterval();
90
        }
91
92 261
        if ($duration instanceof DateInterval) {
93 33
            $new = new self('PT0S');
94 33
            foreach ($duration as $name => $value) {
95 33
                if (property_exists($new, $name)) {
96 33
                    $new->$name = $value;
97
                }
98
            }
99
100 33
            return $new;
101
        }
102
103 228
        if (false !== ($second = filter_var($duration, FILTER_VALIDATE_INT))) {
104 60
            return new self('PT'.$second.'S');
105
        }
106
107 168
        if (!is_string($duration) && !method_exists($duration, '__toString')) {
108 9
            throw new TypeError(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, gettype($duration)));
109
        }
110
111 159
        $duration = (string) $duration;
112 159
        if (1 !== preg_match(self::REGEXP_CHRONO_FORMAT, $duration, $matches)) {
113 141
            return self::createFromDateString($duration);
114
        }
115
116 18
        $matches['fraction'] = str_pad($matches['fraction'] ?? '0000000', 6, '0');
117 18
        $instance = self::createFromDateString(
118 18
            $matches['hour'].' hours '.
119 18
            $matches['minute'].' minutes '.
120 18
            $matches['second'].' seconds '.$matches['fraction'].' microseconds'
121
        );
122 18
        if ('-' === $matches['sign']) {
123 6
            $instance->invert = 1;
124
        }
125
126 18
        return $instance;
127
    }
128
129
    /**
130
     * @inheritdoc
131
     *
132
     * @param mixed $duration a date with relative parts
133
     */
134 162
    public static function createFromDateString($duration): self
135
    {
136 162
        $duration = parent::createFromDateString($duration);
137 162
        $new = new self('PT0S');
138 162
        foreach ($duration as $name => $value) {
139 162
            $new->$name = $value;
140
        }
141
142 162
        return $new;
143
    }
144
145
    /**
146
     * DEPRECATION WARNING! This method will be removed in the next major point release.
147
     *
148
     * @deprecated deprecated since version 4.5
149
     * @see ::format
150
     *
151
     * Returns the ISO8601 interval string representation.
152
     *
153
     * Microseconds fractions are included
154
     */
155 60
    public function __toString(): string
156
    {
157 60
        $date = 'P';
158 60
        foreach (['Y' => 'y', 'M' => 'm', 'D' => 'd'] as $key => $value) {
159 60
            if (0 !== $this->$value) {
160 46
                $date .= '%'.$value.$key;
161
            }
162
        }
163
164 60
        $time = 'T';
165 60
        foreach (['H' => 'h', 'M' => 'i'] as $key => $value) {
166 60
            if (0 !== $this->$value) {
167 47
                $time .= '%'.$value.$key;
168
            }
169
        }
170
171 60
        if (0.0 !== $this->f) {
0 ignored issues
show
introduced by
The condition 0.0 !== $this->f is always true.
Loading history...
172 18
            $second = $this->s + $this->f;
173 18
            if (0 > $this->s) {
174
                $second = $this->s - $this->f;
175
            }
176
            
177 18
            $second = rtrim(sprintf('%f', $second), '0');
178
179 18
            return $this->format($date.$time).$second.'S';
180
        }
181
182 42
        if (0 !== $this->s) {
183 15
            $time .= '%sS';
184
185 15
            return $this->format($date.$time);
186
        }
187
        
188 27
        if ('T' !== $time) {
189 6
            return $this->format($date.$time);
190
        }
191
192 21
        if ('P' !== $date) {
193 18
            return $this->format($date);
194
        }
195
196 3
        return 'PT0S';
197
    }
198
199
    /**
200
     * Returns a new instance with recalculate time and date segments to remove carry over points.
201
     *
202
     * This method MUST retain the state of the current instance, and return
203
     * an instance that contains the time and date segments recalculate to remove
204
     * carry over points.
205
     *
206
     * @param mixed $reference_date a Reference datepoint
207
     *                              by default uses the epoch time
208
     *                              accepts the same input as {@see \League\Period\Datepoint::create}
209
     */
210 12
    public function withoutCarryOver($reference_date = 0): self
211
    {
212 12
        if (!$reference_date instanceof DateTimeImmutable) {
213 12
            $reference_date = Datepoint::create($reference_date);
214
        }
215
216 12
        $duration = self::create($reference_date->diff($reference_date->add($this)));
217 12
        if ($duration == $this) {
218
            return $this;
219
        }
220
221 12
        return $duration;
222
    }
223
}
224