added floor and ceil rounding modes (#695)
This commit is contained in:
committed by
Kevin Papst
parent
136df3724f
commit
3251b26d9e
78
src/Timesheet/Rounding/CeilRounding.php
Normal file
78
src/Timesheet/Rounding/CeilRounding.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai time-tracking app.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Timesheet\Rounding;
|
||||||
|
|
||||||
|
use App\Entity\Timesheet;
|
||||||
|
|
||||||
|
class CeilRounding implements RoundingInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Timesheet $record
|
||||||
|
* @param int $minutes
|
||||||
|
*/
|
||||||
|
public function roundBegin(Timesheet $record, $minutes)
|
||||||
|
{
|
||||||
|
if ($minutes <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timestamp = $record->getBegin()->getTimestamp();
|
||||||
|
$seconds = $minutes * 60;
|
||||||
|
$diff = $timestamp % $seconds;
|
||||||
|
|
||||||
|
if (0 === $diff) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$record->getBegin()->setTimestamp($timestamp - $diff + $seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Timesheet $record
|
||||||
|
* @param int $minutes
|
||||||
|
*/
|
||||||
|
public function roundEnd(Timesheet $record, $minutes)
|
||||||
|
{
|
||||||
|
if ($minutes <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timestamp = $record->getEnd()->getTimestamp();
|
||||||
|
$seconds = $minutes * 60;
|
||||||
|
$diff = $timestamp % $seconds;
|
||||||
|
|
||||||
|
if (0 === $diff) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$record->getEnd()->setTimestamp($timestamp - $diff + $seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Timesheet $record
|
||||||
|
* @param int $minutes
|
||||||
|
*/
|
||||||
|
public function roundDuration(Timesheet $record, $minutes)
|
||||||
|
{
|
||||||
|
if ($minutes <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timestamp = $record->getDuration();
|
||||||
|
$seconds = $minutes * 60;
|
||||||
|
$diff = $timestamp % $seconds;
|
||||||
|
|
||||||
|
if (0 === $diff) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$record->setDuration($timestamp - $diff + $seconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/Timesheet/Rounding/FloorRounding.php
Normal file
78
src/Timesheet/Rounding/FloorRounding.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai time-tracking app.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Timesheet\Rounding;
|
||||||
|
|
||||||
|
use App\Entity\Timesheet;
|
||||||
|
|
||||||
|
class FloorRounding implements RoundingInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Timesheet $record
|
||||||
|
* @param int $minutes
|
||||||
|
*/
|
||||||
|
public function roundBegin(Timesheet $record, $minutes)
|
||||||
|
{
|
||||||
|
if ($minutes <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timestamp = $record->getBegin()->getTimestamp();
|
||||||
|
$seconds = $minutes * 60;
|
||||||
|
$diff = $timestamp % $seconds;
|
||||||
|
|
||||||
|
if (0 === $diff) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$record->getBegin()->setTimestamp($timestamp - $diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Timesheet $record
|
||||||
|
* @param int $minutes
|
||||||
|
*/
|
||||||
|
public function roundEnd(Timesheet $record, $minutes)
|
||||||
|
{
|
||||||
|
if ($minutes <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timestamp = $record->getEnd()->getTimestamp();
|
||||||
|
$seconds = $minutes * 60;
|
||||||
|
$diff = $timestamp % $seconds;
|
||||||
|
|
||||||
|
if (0 === $diff) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$record->getEnd()->setTimestamp($timestamp - $diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Timesheet $record
|
||||||
|
* @param int $minutes
|
||||||
|
*/
|
||||||
|
public function roundDuration(Timesheet $record, $minutes)
|
||||||
|
{
|
||||||
|
if ($minutes <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timestamp = $record->getDuration();
|
||||||
|
$seconds = $minutes * 60;
|
||||||
|
$diff = $timestamp % $seconds;
|
||||||
|
|
||||||
|
if (0 === $diff) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$record->setDuration($timestamp - $diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
152
tests/Timesheet/Rounding/CeilRoundingTest.php
Normal file
152
tests/Timesheet/Rounding/CeilRoundingTest.php
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai time-tracking app.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Tests\Timesheet\Calculator;
|
||||||
|
|
||||||
|
use App\Entity\Timesheet;
|
||||||
|
use App\Timesheet\Rounding\CeilRounding;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \App\Timesheet\Rounding\CeilRounding
|
||||||
|
*/
|
||||||
|
class CeilRoundingTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider getTestData
|
||||||
|
*/
|
||||||
|
public function testCalculate($roundBegin, $roundEnd, $roundDuration, \DateTime $start, \DateTime $end, \DateTime $expectedStart, \DateTime $expectedEnd, $expectedDuration)
|
||||||
|
{
|
||||||
|
$record = new Timesheet();
|
||||||
|
$record->setBegin($start);
|
||||||
|
$record->setEnd($end);
|
||||||
|
$this->assertEquals(0, $record->getDuration());
|
||||||
|
|
||||||
|
$record->setDuration($record->getEnd()->getTimestamp() - $record->getBegin()->getTimestamp());
|
||||||
|
|
||||||
|
$sut = new CeilRounding();
|
||||||
|
$sut->roundBegin($record, $roundBegin);
|
||||||
|
$sut->roundEnd($record, $roundEnd);
|
||||||
|
$record->setDuration($record->getEnd()->getTimestamp() - $record->getBegin()->getTimestamp());
|
||||||
|
$sut->roundDuration($record, $roundDuration);
|
||||||
|
|
||||||
|
$this->assertEquals($expectedStart->getTimestamp(), $record->getBegin()->getTimestamp());
|
||||||
|
$this->assertEquals($expectedEnd->getTimestamp(), $record->getEnd()->getTimestamp());
|
||||||
|
$this->assertEquals($expectedDuration, $record->getDuration());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTestData()
|
||||||
|
{
|
||||||
|
$start = new \DateTime();
|
||||||
|
$start->setTime(12, 0, 0);
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
$start,
|
||||||
|
(clone $start)->setTimestamp($start->getTimestamp() + 1837),
|
||||||
|
$start,
|
||||||
|
(clone $start)->setTimestamp($start->getTimestamp() + 1837),
|
||||||
|
1837
|
||||||
|
],
|
||||||
|
[
|
||||||
|
15,
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
(clone $start)->setTime(12, 17, 35),
|
||||||
|
(clone $start)->setTime(13, 32, 52),
|
||||||
|
(clone $start)->setTime(12, 30, 00),
|
||||||
|
(clone $start)->setTime(13, 45, 00),
|
||||||
|
4500
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
(clone $start)->setTime(12, 17, 35),
|
||||||
|
(clone $start)->setTime(13, 32, 52),
|
||||||
|
(clone $start)->setTime(12, 17, 35),
|
||||||
|
(clone $start)->setTime(13, 32, 52),
|
||||||
|
4517
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
(clone $start)->setTime(12, 17, 35),
|
||||||
|
(clone $start)->setTime(13, 32, 52),
|
||||||
|
(clone $start)->setTime(12, 18, 00),
|
||||||
|
(clone $start)->setTime(13, 33, 00),
|
||||||
|
4500
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
30,
|
||||||
|
(clone $start)->setTime(12, 10, 51),
|
||||||
|
(clone $start)->setTime(14, 40, 52),
|
||||||
|
(clone $start)->setTime(12, 10, 51),
|
||||||
|
(clone $start)->setTime(14, 40, 52),
|
||||||
|
10800
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
30,
|
||||||
|
(clone $start)->setTime(12, 27, 35), // 12:15
|
||||||
|
(clone $start)->setTime(14, 32, 52), // 14:33 => 2:18 => 2:30
|
||||||
|
(clone $start)->setTime(12, 27, 35),
|
||||||
|
(clone $start)->setTime(14, 33, 00),
|
||||||
|
9000
|
||||||
|
],
|
||||||
|
[
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
30,
|
||||||
|
(clone $start)->setTime(12, 27, 35), // 12:15
|
||||||
|
(clone $start)->setTime(14, 32, 52), // 14:33 => 2:18 (second duration will not be rounded)
|
||||||
|
(clone $start)->setTime(12, 30, 00), // 12:15
|
||||||
|
(clone $start)->setTime(14, 32, 52), // 14:33 => 2:18 (second duration will not be rounded)
|
||||||
|
9000
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
(clone $start)->setTime(12, 27, 35), // no diff, to test ...
|
||||||
|
(clone $start)->setTime(12, 27, 35), // ... that no rounding is applied
|
||||||
|
(clone $start)->setTime(12, 27, 35), // no diff, to test ...
|
||||||
|
(clone $start)->setTime(12, 27, 35), // ... that no rounding is applied
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
(clone $start)->setTime(12, 27, 00), // no diff, to test ...
|
||||||
|
(clone $start)->setTime(12, 27, 00), // ... that no rounding is applied
|
||||||
|
(clone $start)->setTime(12, 27, 00), // no diff, to test ...
|
||||||
|
(clone $start)->setTime(12, 27, 00), // ... that no rounding is applied
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
(clone $start)->setTime(12, 27, 35), // no diff, to test ...
|
||||||
|
(clone $start)->setTime(12, 27, 35), // ... that no rounding is applied
|
||||||
|
(clone $start)->setTime(12, 27, 35), // no diff, to test ...
|
||||||
|
(clone $start)->setTime(12, 27, 35), // ... that no rounding is applied
|
||||||
|
0
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
152
tests/Timesheet/Rounding/FloorRoundingTest.php
Normal file
152
tests/Timesheet/Rounding/FloorRoundingTest.php
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai time-tracking app.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Tests\Timesheet\Calculator;
|
||||||
|
|
||||||
|
use App\Entity\Timesheet;
|
||||||
|
use App\Timesheet\Rounding\FloorRounding;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \App\Timesheet\Rounding\FloorRounding
|
||||||
|
*/
|
||||||
|
class FloorRoundingTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider getTestData
|
||||||
|
*/
|
||||||
|
public function testCalculate($roundBegin, $roundEnd, $roundDuration, \DateTime $start, \DateTime $end, \DateTime $expectedStart, \DateTime $expectedEnd, $expectedDuration)
|
||||||
|
{
|
||||||
|
$record = new Timesheet();
|
||||||
|
$record->setBegin($start);
|
||||||
|
$record->setEnd($end);
|
||||||
|
$this->assertEquals(0, $record->getDuration());
|
||||||
|
|
||||||
|
$record->setDuration($record->getEnd()->getTimestamp() - $record->getBegin()->getTimestamp());
|
||||||
|
|
||||||
|
$sut = new FloorRounding();
|
||||||
|
$sut->roundBegin($record, $roundBegin);
|
||||||
|
$sut->roundEnd($record, $roundEnd);
|
||||||
|
$record->setDuration($record->getEnd()->getTimestamp() - $record->getBegin()->getTimestamp());
|
||||||
|
$sut->roundDuration($record, $roundDuration);
|
||||||
|
|
||||||
|
$this->assertEquals($expectedStart->getTimestamp(), $record->getBegin()->getTimestamp());
|
||||||
|
$this->assertEquals($expectedEnd->getTimestamp(), $record->getEnd()->getTimestamp());
|
||||||
|
$this->assertEquals($expectedDuration, $record->getDuration());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTestData()
|
||||||
|
{
|
||||||
|
$start = new \DateTime();
|
||||||
|
$start->setTime(12, 0, 0);
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
$start,
|
||||||
|
(clone $start)->setTimestamp($start->getTimestamp() + 1837),
|
||||||
|
$start,
|
||||||
|
(clone $start)->setTimestamp($start->getTimestamp() + 1837),
|
||||||
|
1837
|
||||||
|
],
|
||||||
|
[
|
||||||
|
15,
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
(clone $start)->setTime(12, 17, 35),
|
||||||
|
(clone $start)->setTime(13, 32, 52),
|
||||||
|
(clone $start)->setTime(12, 15, 00),
|
||||||
|
(clone $start)->setTime(13, 30, 00),
|
||||||
|
4500
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
(clone $start)->setTime(12, 17, 35),
|
||||||
|
(clone $start)->setTime(13, 32, 52),
|
||||||
|
(clone $start)->setTime(12, 17, 35),
|
||||||
|
(clone $start)->setTime(13, 32, 52),
|
||||||
|
4517
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
(clone $start)->setTime(12, 17, 35),
|
||||||
|
(clone $start)->setTime(13, 32, 52),
|
||||||
|
(clone $start)->setTime(12, 17, 00),
|
||||||
|
(clone $start)->setTime(13, 32, 00),
|
||||||
|
4500
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
30,
|
||||||
|
(clone $start)->setTime(12, 10, 51),
|
||||||
|
(clone $start)->setTime(14, 40, 52),
|
||||||
|
(clone $start)->setTime(12, 10, 51),
|
||||||
|
(clone $start)->setTime(14, 40, 52),
|
||||||
|
9000
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
30,
|
||||||
|
(clone $start)->setTime(12, 27, 35), // 12:15
|
||||||
|
(clone $start)->setTime(14, 32, 52), // 14:33 => 2:18 => 2:30
|
||||||
|
(clone $start)->setTime(12, 27, 35),
|
||||||
|
(clone $start)->setTime(14, 32, 00),
|
||||||
|
7200
|
||||||
|
],
|
||||||
|
[
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
30,
|
||||||
|
(clone $start)->setTime(12, 27, 35), // 12:15
|
||||||
|
(clone $start)->setTime(14, 32, 52), // 14:33 => 2:18 (second duration will not be rounded)
|
||||||
|
(clone $start)->setTime(12, 15, 00), // 12:15
|
||||||
|
(clone $start)->setTime(14, 32, 52), // 14:33 => 2:18 (second duration will not be rounded)
|
||||||
|
7200
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
(clone $start)->setTime(12, 27, 35), // no diff, to test ...
|
||||||
|
(clone $start)->setTime(12, 27, 35), // ... that no rounding is applied
|
||||||
|
(clone $start)->setTime(12, 27, 35), // no diff, to test ...
|
||||||
|
(clone $start)->setTime(12, 27, 35), // ... that no rounding is applied
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
(clone $start)->setTime(12, 27, 00), // no diff, to test ...
|
||||||
|
(clone $start)->setTime(12, 27, 00), // ... that no rounding is applied
|
||||||
|
(clone $start)->setTime(12, 27, 00), // no diff, to test ...
|
||||||
|
(clone $start)->setTime(12, 27, 00), // ... that no rounding is applied
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
(clone $start)->setTime(12, 27, 35), // no diff, to test ...
|
||||||
|
(clone $start)->setTime(12, 27, 35), // ... that no rounding is applied
|
||||||
|
(clone $start)->setTime(12, 27, 35), // no diff, to test ...
|
||||||
|
(clone $start)->setTime(12, 27, 35), // ... that no rounding is applied
|
||||||
|
0
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user