Passed
Pull Request — master (#76)
by ignace nyamagana
03:49
created

Duration::__toString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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