Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 31 additions & 10 deletions src/main/php/util/TimeSpan.class.php
Original file line number Diff line number Diff line change
@@ -1,27 +1,48 @@
<?php namespace util;

use DateInterval, Exception;
use lang\{Value, IllegalArgumentException, IllegalStateException};

/**
* Represents a span of time
*
* @see util.Dates::diff()
* @test net.xp_framework.unittest.util.TimeSpanTest
* @see util.Dates::diff()
* @test net.xp_framework.unittest.util.TimeSpanTest
*/
class TimeSpan implements Value {
protected $_seconds = 0;
protected $_seconds;

/**
* Contructor
* Constructor
*
* @param int secs - an amount of seconds, absolute value is used
* @throws lang.IllegalArgumentException in case the value given is not numeric
* @param int|string|DateInterval $arg
* @throws lang.IllegalArgumentException
*/
public function __construct($secs= 0) {
if (!is_numeric($secs)) {
throw new IllegalArgumentException('Given argument is not an integer: '.typeof($secs)->getName());
public function __construct($arg) {
if (is_numeric($arg)) {
$this->_seconds= (int)abs($arg);
} else {
try {
if ($arg instanceof DateInterval) {
$i= $arg;
} else if ('P' === $arg[0] ?? null) {
$i= new DateInterval($arg);
} else {
$i= DateInterval::createFromDateString($arg);
}
} catch (Exception $e) {
throw new IllegalArgumentException('Invalid time span '.Objects::stringOf($arg), $e);
}

// PHP < 8.3 returns false for invalid time spans
if (false === $i) throw new IllegalArgumentException('Invalid time span '.Objects::stringOf($arg));

// Years and months do not have a constant amount of seconds. Technically, days
// don't either (think: leap seconds), but we'll handle them as 86400 seconds
if ($i->y || $i->m) throw new IllegalArgumentException('Cannot create from interval with years / months');

$this->_seconds= $i->d * 86400 + $i->h * 3600 + $i->i * 60 + $i->s;
}
$this->_seconds= (int)abs($secs);
}

/**
Expand Down
83 changes: 48 additions & 35 deletions src/test/php/util/unittest/TimeSpanTest.class.php
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
<?php namespace util\unittest;

use DateInterval;
use lang\{IllegalArgumentException, IllegalStateException};
use test\{Assert, Expect, Test};
use test\{Assert, Expect, Test, Values};
use util\TimeSpan;

class TimeSpanTest {

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

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

#[Test, Expect(IllegalArgumentException::class)]
public function wrongArguments() {
new TimeSpan('2 days');
#[Test, Expect(IllegalArgumentException::class), Values([null, '', 'not a time span', false, true])]
public function invalid_string_argument($arg) {
new TimeSpan($arg);
}

#[Test, Expect(IllegalArgumentException::class), Values(['P1Y', 'P1M'])]
public function unsupported_dateinterval($arg) {
new TimeSpan(new DateInterval($arg));
}

#[Test]
public function span_from_dateinterval() {
Assert::equals('1d, 2h, 0m, 0s', (new TimeSpan(new DateInterval('P1DT2H')))->toString());
}

#[Test]
Expand All @@ -33,63 +44,65 @@ public function add() {
public function subtract() {
Assert::equals('0d, 22h, 58m, 55s', (new TimeSpan(86400))
->substract(new TimeSpan(3600), new TimeSpan(60))
->substract(new TimeSpan(5))->toString()
->substract(new TimeSpan(5))
->toString()
);
}

#[Test]
public function subtractToZero() {
public function subtract_resulting_in_zero() {
Assert::equals(
'0d, 0h, 0m, 0s',
(new TimeSpan(6100))->substract(new TimeSpan(6100))->toString()
);
}

#[Test, Expect(IllegalStateException::class)]
public function subtractToNegative() {
public function subtract_may_not_result_in_negative_values() {
(new TimeSpan(0))->substract(new TimeSpan(1));
}

#[Test]
public function addAndSubstract() {
public function add_and_subtract() {
Assert::equals('1d, 1h, 0m, 55s', (new TimeSpan(86400))
->add(new TimeSpan(3600), new TimeSpan(60))
->substract(new TimeSpan(5))->toString()
->substract(new TimeSpan(5))
->toString()
);
}

#[Test, Expect(IllegalArgumentException::class)]
public function addWrongArguments() {
public function incorrect_argument_to_add() {
(new TimeSpan(0))->add('2 days');
}

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

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

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

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

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

#[Test]
public function wholeValues() {
public function whole_values() {
$t= new TimeSpan(91865);
Assert::equals(5, $t->getWholeSeconds(), 'wholeSeconds');
Assert::equals(31, $t->getWholeMinutes(), 'wholeMinutes');
Expand All @@ -98,77 +111,77 @@ public function wholeValues() {
}

#[Test]
public function formatSeconds() {
public function format_seconds() {
Assert::equals('91865', (new TimeSpan(91865))->format('%s'));
}

#[Test]
public function formatWholeSeconds() {
public function format_whole_seconds() {
Assert::equals('5', (new TimeSpan(91865))->format('%w'));
}

#[Test]
public function formatMinutes() {
public function format_minutes() {
Assert::equals('1531', (new TimeSpan(91865))->format('%m'));
}

#[Test]
public function formatFloatMinutes() {
public function format_float_minutes() {
Assert::equals('1531.08', (new TimeSpan(91865))->format('%M'));
}

#[Test]
public function formatWholeMinutes() {
public function format_whole_minutes() {
Assert::equals('31', (new TimeSpan(91865))->format('%j'));
}

#[Test]
public function formatHours() {
public function format_hours() {
Assert::equals('25', (new TimeSpan(91865))->format('%h'));
}

#[Test]
public function formatFloatHours() {
public function format_float_hours() {
Assert::equals('25.52', (new TimeSpan(91865))->format('%H'));
}

#[Test]
public function formatWholeHours() {
public function format_whole_hours() {
Assert::equals('1', (new TimeSpan(91865))->format('%y'));
}

#[Test]
public function formatDays() {
public function format_days() {
Assert::equals('1', (new TimeSpan(91865))->format('%d'));
}

#[Test]
public function formatFloatDays() {
public function format_float_days() {
Assert::equals('1.06', (new TimeSpan(91865))->format('%D'));
}

#[Test]
public function formatWholeDays() {
public function format_whole_days() {
Assert::equals('1', (new TimeSpan(91865))->format('%e'));
}

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

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

#[Test]
public function compareTwoEqualInstances() {
public function compare_two_equal_instances() {
Assert::equals(new TimeSpan(3600), new TimeSpan(3600));
}

#[Test]
public function compareTwoUnequalInstances() {
public function compare_two_differing_instances() {
Assert::notEquals(new TimeSpan(3600), new TimeSpan(0));
}
}