Skip to content

Commit 5a835db

Browse files
authored
Merge pull request #355 from thekid/feature/dateinterval
Work with `DateInterval` instances in util.TimeSpan
2 parents ca7dc7f + 88fedec commit 5a835db

File tree

2 files changed

+79
-45
lines changed

2 files changed

+79
-45
lines changed

src/main/php/util/TimeSpan.class.php

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,48 @@
11
<?php namespace util;
22

3+
use DateInterval, Exception;
34
use lang\{Value, IllegalArgumentException, IllegalStateException};
45

56
/**
67
* Represents a span of time
78
*
8-
* @see util.Dates::diff()
9-
* @test net.xp_framework.unittest.util.TimeSpanTest
9+
* @see util.Dates::diff()
10+
* @test net.xp_framework.unittest.util.TimeSpanTest
1011
*/
1112
class TimeSpan implements Value {
12-
protected $_seconds = 0;
13+
protected $_seconds;
1314

1415
/**
15-
* Contructor
16+
* Constructor
1617
*
17-
* @param int secs - an amount of seconds, absolute value is used
18-
* @throws lang.IllegalArgumentException in case the value given is not numeric
18+
* @param int|string|DateInterval $arg
19+
* @throws lang.IllegalArgumentException
1920
*/
20-
public function __construct($secs= 0) {
21-
if (!is_numeric($secs)) {
22-
throw new IllegalArgumentException('Given argument is not an integer: '.typeof($secs)->getName());
21+
public function __construct($arg) {
22+
if (is_numeric($arg)) {
23+
$this->_seconds= (int)abs($arg);
24+
} else {
25+
try {
26+
if ($arg instanceof DateInterval) {
27+
$i= $arg;
28+
} else if ('P' === $arg[0] ?? null) {
29+
$i= new DateInterval($arg);
30+
} else {
31+
$i= DateInterval::createFromDateString($arg);
32+
}
33+
} catch (Exception $e) {
34+
throw new IllegalArgumentException('Invalid time span '.Objects::stringOf($arg), $e);
35+
}
36+
37+
// PHP < 8.3 returns false for invalid time spans
38+
if (false === $i) throw new IllegalArgumentException('Invalid time span '.Objects::stringOf($arg));
39+
40+
// Years and months do not have a constant amount of seconds. Technically, days
41+
// don't either (think: leap seconds), but we'll handle them as 86400 seconds
42+
if ($i->y || $i->m) throw new IllegalArgumentException('Cannot create from interval with years / months');
43+
44+
$this->_seconds= $i->d * 86400 + $i->h * 3600 + $i->i * 60 + $i->s;
2345
}
24-
$this->_seconds= (int)abs($secs);
2546
}
2647

2748
/**
Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,35 @@
11
<?php namespace util\unittest;
22

3+
use DateInterval;
34
use lang\{IllegalArgumentException, IllegalStateException};
4-
use test\{Assert, Expect, Test};
5+
use test\{Assert, Expect, Test, Values};
56
use util\TimeSpan;
67

78
class TimeSpanTest {
89

9-
#[Test]
10-
public function newTimeSpan() {
11-
Assert::equals('0d, 2h, 1m, 5s', (new TimeSpan(7265))->toString());
10+
#[Test, Values([7265, 7265.0, 'PT2H1M5S', '2 hours 1 minute 5 seconds'])]
11+
public function span_from($arg) {
12+
Assert::equals('0d, 2h, 1m, 5s', (new TimeSpan($arg))->toString());
1213
}
1314

1415
#[Test]
15-
public function newNegativeTimeSpan() {
16+
public function negative_span() {
1617
Assert::equals('0d, 0h, 0m, 1s', (new TimeSpan(-1))->toString());
1718
}
1819

19-
#[Test, Expect(IllegalArgumentException::class)]
20-
public function wrongArguments() {
21-
new TimeSpan('2 days');
20+
#[Test, Expect(IllegalArgumentException::class), Values([null, '', 'not a time span', false, true])]
21+
public function invalid_string_argument($arg) {
22+
new TimeSpan($arg);
23+
}
24+
25+
#[Test, Expect(IllegalArgumentException::class), Values(['P1Y', 'P1M'])]
26+
public function unsupported_dateinterval($arg) {
27+
new TimeSpan(new DateInterval($arg));
28+
}
29+
30+
#[Test]
31+
public function span_from_dateinterval() {
32+
Assert::equals('1d, 2h, 0m, 0s', (new TimeSpan(new DateInterval('P1DT2H')))->toString());
2233
}
2334

2435
#[Test]
@@ -33,63 +44,65 @@ public function add() {
3344
public function subtract() {
3445
Assert::equals('0d, 22h, 58m, 55s', (new TimeSpan(86400))
3546
->substract(new TimeSpan(3600), new TimeSpan(60))
36-
->substract(new TimeSpan(5))->toString()
47+
->substract(new TimeSpan(5))
48+
->toString()
3749
);
3850
}
3951

4052
#[Test]
41-
public function subtractToZero() {
53+
public function subtract_resulting_in_zero() {
4254
Assert::equals(
4355
'0d, 0h, 0m, 0s',
4456
(new TimeSpan(6100))->substract(new TimeSpan(6100))->toString()
4557
);
4658
}
4759

4860
#[Test, Expect(IllegalStateException::class)]
49-
public function subtractToNegative() {
61+
public function subtract_may_not_result_in_negative_values() {
5062
(new TimeSpan(0))->substract(new TimeSpan(1));
5163
}
5264

5365
#[Test]
54-
public function addAndSubstract() {
66+
public function add_and_subtract() {
5567
Assert::equals('1d, 1h, 0m, 55s', (new TimeSpan(86400))
5668
->add(new TimeSpan(3600), new TimeSpan(60))
57-
->substract(new TimeSpan(5))->toString()
69+
->substract(new TimeSpan(5))
70+
->toString()
5871
);
5972
}
6073

6174
#[Test, Expect(IllegalArgumentException::class)]
62-
public function addWrongArguments() {
75+
public function incorrect_argument_to_add() {
6376
(new TimeSpan(0))->add('2 days');
6477
}
6578

6679
#[Test]
67-
public function fromSeconds() {
80+
public function from_seconds() {
6881
Assert::equals('0d, 1h, 0m, 0s', TimeSpan::seconds(3600)->toString());
6982
}
7083

7184
#[Test]
72-
public function fromMinutes() {
85+
public function from_minutes() {
7386
Assert::equals('0d, 2h, 7m, 0s', TimeSpan::minutes(127)->toString());
7487
}
7588

7689
#[Test]
77-
public function fromHours() {
90+
public function from_hours() {
7891
Assert::equals('1d, 3h, 0m, 0s', TimeSpan::hours(27)->toString());
7992
}
8093

8194
#[Test]
82-
public function fromDays() {
95+
public function from_days() {
8396
Assert::equals('40d, 0h, 0m, 0s', TimeSpan::days(40)->toString());
8497
}
8598

8699
#[Test]
87-
public function fromWeeks() {
100+
public function from_weeks() {
88101
Assert::equals('7d, 0h, 0m, 0s', TimeSpan::weeks(1)->toString());
89102
}
90103

91104
#[Test]
92-
public function wholeValues() {
105+
public function whole_values() {
93106
$t= new TimeSpan(91865);
94107
Assert::equals(5, $t->getWholeSeconds(), 'wholeSeconds');
95108
Assert::equals(31, $t->getWholeMinutes(), 'wholeMinutes');
@@ -98,77 +111,77 @@ public function wholeValues() {
98111
}
99112

100113
#[Test]
101-
public function formatSeconds() {
114+
public function format_seconds() {
102115
Assert::equals('91865', (new TimeSpan(91865))->format('%s'));
103116
}
104117

105118
#[Test]
106-
public function formatWholeSeconds() {
119+
public function format_whole_seconds() {
107120
Assert::equals('5', (new TimeSpan(91865))->format('%w'));
108121
}
109122

110123
#[Test]
111-
public function formatMinutes() {
124+
public function format_minutes() {
112125
Assert::equals('1531', (new TimeSpan(91865))->format('%m'));
113126
}
114127

115128
#[Test]
116-
public function formatFloatMinutes() {
129+
public function format_float_minutes() {
117130
Assert::equals('1531.08', (new TimeSpan(91865))->format('%M'));
118131
}
119132

120133
#[Test]
121-
public function formatWholeMinutes() {
134+
public function format_whole_minutes() {
122135
Assert::equals('31', (new TimeSpan(91865))->format('%j'));
123136
}
124137

125138
#[Test]
126-
public function formatHours() {
139+
public function format_hours() {
127140
Assert::equals('25', (new TimeSpan(91865))->format('%h'));
128141
}
129142

130143
#[Test]
131-
public function formatFloatHours() {
144+
public function format_float_hours() {
132145
Assert::equals('25.52', (new TimeSpan(91865))->format('%H'));
133146
}
134147

135148
#[Test]
136-
public function formatWholeHours() {
149+
public function format_whole_hours() {
137150
Assert::equals('1', (new TimeSpan(91865))->format('%y'));
138151
}
139152

140153
#[Test]
141-
public function formatDays() {
154+
public function format_days() {
142155
Assert::equals('1', (new TimeSpan(91865))->format('%d'));
143156
}
144157

145158
#[Test]
146-
public function formatFloatDays() {
159+
public function format_float_days() {
147160
Assert::equals('1.06', (new TimeSpan(91865))->format('%D'));
148161
}
149162

150163
#[Test]
151-
public function formatWholeDays() {
164+
public function format_whole_days() {
152165
Assert::equals('1', (new TimeSpan(91865))->format('%e'));
153166
}
154167

155168
#[Test]
156-
public function format() {
169+
public function format_() {
157170
Assert::equals('1d1h', (new TimeSpan(91865))->format('%ed%yh'));
158171
}
159172

160173
#[Test]
161-
public function formatPercent() {
174+
public function format_percent() {
162175
Assert::equals('%1d%1h%', (new TimeSpan(91865))->format('%%%ed%%%yh%%'));
163176
}
164177

165178
#[Test]
166-
public function compareTwoEqualInstances() {
179+
public function compare_two_equal_instances() {
167180
Assert::equals(new TimeSpan(3600), new TimeSpan(3600));
168181
}
169182

170183
#[Test]
171-
public function compareTwoUnequalInstances() {
184+
public function compare_two_differing_instances() {
172185
Assert::notEquals(new TimeSpan(3600), new TimeSpan(0));
173186
}
174187
}

0 commit comments

Comments
 (0)