Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
* Calculate microseconds with integer precision
* Fix for PHP bug 77145 https://bugs.php.net/bug.php?id=77145
  • Loading branch information
kylekatarnls committed Nov 13, 2018
1 parent 1275d16 commit d5603a1
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 35 deletions.
95 changes: 70 additions & 25 deletions src/Carbon/Traits/Difference.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Carbon\CarbonPeriod;
use Carbon\Translator;
use Closure;
use DateInterval;
use DateTimeInterface;

/**
Expand All @@ -26,47 +27,88 @@
* Depends on the following methods:
*
* @method bool lessThan($date)
* @method \DateInterval diff(\DateTimeInterface $date)
* @method DateInterval diff(\DateTimeInterface $date)
* @method CarbonInterface copy()
* @method static Translator translator()
*/
trait Difference
{
/**
* Get the difference as a CarbonInterval instance
*
* @param \Carbon\Carbon|\DateTimeInterface|string|null $date
* @param bool $absolute Get the absolute of the difference
* @param DateInterval $diff
* @param bool $absolute
*
* @return CarbonInterval
*/
public function diffAsCarbonInterval($date = null, $absolute = true)
{
$diff = CarbonInterval::instance($this->diff($this->resolveCarbon($date), $absolute));
// Work-around for https://github.com/briannesbitt/Carbon/issues/1503
if ($diff->f < 0) {
$diff->f++;
$diff->s--;

if ($diff->s < 0) {
$diff->s += 60;
$diff->i--;

if ($diff->i < 0) {
$diff->i += 60;
$diff->h--;

if ($diff->h < 0) {
$diff->i += 24;
$diff->d--;
protected static function fixDiffInterval(DateInterval $diff, $absolute)
{
$diff = CarbonInterval::instance($diff);
// Work-around for https://bugs.php.net/bug.php?id=77145
// @codeCoverageIgnoreStart
if ($diff->f > 0 && $diff->y === -1 && $diff->m === 11 && $diff->d === 29 && $diff->h === 23 && $diff->i === 59 && $diff->s === 59) {
$diff->y = 0;
$diff->m = 0;
$diff->d = 0;
$diff->h = 0;
$diff->i = 0;
$diff->s = 0;
$diff->f = (1000000 - round($diff->f * 1000000)) / 1000000;
$diff->invert();
} elseif ($diff->f < 0) {
if ($diff->s !== 0 || $diff->i !== 0 || $diff->h !== 0 || $diff->d !== 0 || $diff->m !== 0 || $diff->y !== 0) {
$diff->f = (round($diff->f * 1000000) + 1000000) / 1000000;
$diff->s--;

if ($diff->s < 0) {
$diff->s += 60;
$diff->i--;

if ($diff->i < 0) {
$diff->i += 60;
$diff->h--;

if ($diff->h < 0) {
$diff->i += 24;
$diff->d--;

if ($diff->d < 0) {
$diff->d += 30;
$diff->m--;

if ($diff->m < 0) {
$diff->m += 12;
$diff->y--;
}
}
}
}
}
} else {
$diff->f *= -1;
$diff->invert();
}
}
// @codeCoverageIgnoreEnd

if ($absolute && $diff->invert) {
$diff->invert();
}

return $diff;
}

/**
* Get the difference as a CarbonInterval instance
*
* @param \Carbon\Carbon|\DateTimeInterface|string|null $date
* @param bool $absolute Get the absolute of the difference
*
* @return CarbonInterval
*/
public function diffAsCarbonInterval($date = null, $absolute = true)
{
return static::fixDiffInterval($this->diff($this->resolveCarbon($date), $absolute), $absolute);
}

/**
* Get the difference in years
*
Expand Down Expand Up @@ -270,10 +312,13 @@ public function diffInRealMinutes($date = null, $absolute = true)
public function diffInSeconds($date = null, $absolute = true)
{
$diff = $this->diff($this->resolveCarbon($date));
if ($diff->days === 0) {
$diff = static::fixDiffInterval($diff, $absolute);
}
$value = ((($diff->days * static::HOURS_PER_DAY) +
$diff->h) * static::MINUTES_PER_HOUR +
$diff->i) * static::SECONDS_PER_MINUTE +
$diff->s - ($diff->f < 0 ? 1 : 0);
$diff->s;

return $absolute || !$diff->invert ? $value : -$value;
}
Expand Down
13 changes: 7 additions & 6 deletions src/Carbon/Traits/Modifiers.php
Original file line number Diff line number Diff line change
Expand Up @@ -325,19 +325,20 @@ public function nthOfYear($nth, $dayOfWeek)
}

/**
* Modify the current instance to the average of a given instance (default now) and the current instance.
* Modify the current instance to the average of a given instance (default now) and the current instance
* (second-precision).
*
* @param \Carbon\Carbon|\DateTimeInterface|null $date
*
* @return static
*/
public function average($date = null)
{
return $this->addSeconds((int) ($this->diffInSeconds($this->resolveCarbon($date), false) / 2));
return $this->addRealSeconds((int) ($this->diffInRealSeconds($this->resolveCarbon($date), false) / 2));
}

/**
* Get the closest date from the instance.
* Get the closest date from the instance (second-precision).
*
* @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
* @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
Expand All @@ -346,11 +347,11 @@ public function average($date = null)
*/
public function closest($date1, $date2)
{
return $this->diffInSeconds($date1) < $this->diffInSeconds($date2) ? $date1 : $date2;
return $this->diffInRealSeconds($date1) < $this->diffInRealSeconds($date2) ? $date1 : $date2;
}

/**
* Get the farthest date from the instance.
* Get the farthest date from the instance (second-precision).
*
* @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
* @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
Expand All @@ -359,7 +360,7 @@ public function closest($date1, $date2)
*/
public function farthest($date1, $date2)
{
return $this->diffInSeconds($date1) > $this->diffInSeconds($date2) ? $date1 : $date2;
return $this->diffInRealSeconds($date1) > $this->diffInRealSeconds($date2) ? $date1 : $date2;
}

/**
Expand Down
30 changes: 30 additions & 0 deletions tests/Carbon/DiffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1531,5 +1531,35 @@ public function testPhpBug77007()
$endDate = Carbon::parse('2018-10-11 20:59:07.237419');

$this->assertSame(0, $startDate->diffInSeconds($endDate));

$startDate = Carbon::parse('2018-10-11 20:59:06.914653');
$endDate = Carbon::parse('2018-10-11 20:59:07.237419');

$this->assertSame('+ 00-00-00 00:00:00.322766', $startDate->diffAsCarbonInterval($endDate)->format('%R %Y-%M-%D %H:%I:%S.%F'));
$this->assertSame(0, $startDate->diffInSeconds($endDate));

$startDate = Carbon::parse('2018-10-11 20:59:06.914653');
$endDate = Carbon::parse('2018-10-11 20:59:05.237419');

$this->assertSame('+ 00-00-00 00:00:01.677234', $startDate->diffAsCarbonInterval($endDate)->format('%R %Y-%M-%D %H:%I:%S.%F'));
$this->assertSame(1, $startDate->diffInSeconds($endDate));

$this->assertSame('- 00-00-00 00:00:01.677234', $startDate->diffAsCarbonInterval($endDate, false)->format('%R %Y-%M-%D %H:%I:%S.%F'));
$this->assertSame(-1, $startDate->diffInSeconds($endDate, false));

$startDate = Carbon::parse('2018-10-11 20:59:06.914653');
$endDate = Carbon::parse('2018-10-11 20:59:06.237419');

$this->assertSame('+ 00-00-00 00:00:00.677234', $startDate->diffAsCarbonInterval($endDate)->format('%R %Y-%M-%D %H:%I:%S.%F'));
$this->assertSame(0, $startDate->diffInSeconds($endDate));

$this->assertSame('- 00-00-00 00:00:00.677234', $startDate->diffAsCarbonInterval($endDate, false)->format('%R %Y-%M-%D %H:%I:%S.%F'));
$this->assertSame(0, $startDate->diffInSeconds($endDate, false));

$startDate = Carbon::parse('2017-12-31 23:59:59.914653');
$endDate = Carbon::parse('2018-01-01 00:00:00.237419');

$this->assertSame('+ 00-00-00 00:00:00.322766', $startDate->diffAsCarbonInterval($endDate)->format('%R %Y-%M-%D %H:%I:%S.%F'));
$this->assertSame(0, $startDate->diffInSeconds($endDate));
}
}
30 changes: 26 additions & 4 deletions tests/CarbonImmutable/DiffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1404,12 +1404,34 @@ public function testPhpBug77007()

$this->assertSame(0, $startDate->diffInSeconds($endDate));

$interval = $startDate->diffAsCarbonInterval($endDate);
$this->assertSame('00:00:00.32276', substr($interval->format('%H:%I:%S.%F'), 0, -1));
$startDate = Carbon::parse('2018-10-11 20:59:06.914653');
$endDate = Carbon::parse('2018-10-11 20:59:07.237419');

$this->assertSame('+ 00-00-00 00:00:00.322766', $startDate->diffAsCarbonInterval($endDate)->format('%R %Y-%M-%D %H:%I:%S.%F'));
$this->assertSame(0, $startDate->diffInSeconds($endDate));

$startDate = Carbon::parse('2018-10-11 20:59:06.914653');
$endDate = Carbon::parse('2018-10-11 20:59:05.237419');

$this->assertSame('+ 00-00-00 00:00:01.677234', $startDate->diffAsCarbonInterval($endDate)->format('%R %Y-%M-%D %H:%I:%S.%F'));
$this->assertSame(1, $startDate->diffInSeconds($endDate));

$this->assertSame('- 00-00-00 00:00:01.677234', $startDate->diffAsCarbonInterval($endDate, false)->format('%R %Y-%M-%D %H:%I:%S.%F'));
$this->assertSame(-1, $startDate->diffInSeconds($endDate, false));

$startDate = Carbon::parse('2018-10-11 20:59:06.914653');
$endDate = Carbon::parse('2018-10-11 20:59:06.237419');

$this->assertSame('+ 00-00-00 00:00:00.677234', $startDate->diffAsCarbonInterval($endDate)->format('%R %Y-%M-%D %H:%I:%S.%F'));
$this->assertSame(0, $startDate->diffInSeconds($endDate));

$this->assertSame('- 00-00-00 00:00:00.677234', $startDate->diffAsCarbonInterval($endDate, false)->format('%R %Y-%M-%D %H:%I:%S.%F'));
$this->assertSame(0, $startDate->diffInSeconds($endDate, false));

$startDate = Carbon::parse('2017-12-31 23:59:59.914653');
$endDate = Carbon::parse('2018-01-01 00:00:00.237419');
$interval = $startDate->diffAsCarbonInterval($endDate);
$this->assertSame('00:00:00.32276', substr($interval->format('%H:%I:%S.%F'), 0, -1));

$this->assertSame('+ 00-00-00 00:00:00.322766', $startDate->diffAsCarbonInterval($endDate)->format('%R %Y-%M-%D %H:%I:%S.%F'));
$this->assertSame(0, $startDate->diffInSeconds($endDate));
}
}

0 comments on commit d5603a1

Please sign in to comment.