Skip to content

Commit

Permalink
Merge pull request #283 from cakephp/diff-ignore
Browse files Browse the repository at this point in the history
Add diffInMonthsIgnoreTimezone()
  • Loading branch information
othercorey authored Jan 16, 2021
2 parents 9d5cfc7 + c96f7b7 commit 3f119d1
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 42 deletions.
52 changes: 29 additions & 23 deletions src/Traits/DifferenceTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,53 +42,59 @@ trait DifferenceTrait
protected static $diffFormatter;

/**
* Peforms a `diff()` without adjusting for timezone.
*
* The normal `diff()` will convert all times to UTC before comparing which can
* result in day and month calculations to be different than in the original timezone.
* Get the difference in years
*
* @param \Cake\Chronos\ChronosInterface $dt The target instance
* @param bool $abs Get the absolute difference
* @return false|\DateInterval
* @param \Cake\Chronos\ChronosInterface|null $dt The instance to difference from.
* @param bool $abs Get the absolute of the difference
* @return int
*/
public function diffIgnoreTimezone(ChronosInterface $dt, bool $abs = true)
public function diffInYears(?ChronosInterface $dt = null, bool $abs = true): int
{
$utcTz = new DateTimeZone('UTC');

$source = $this;
if ($this->getTimezone()->getName() !== 'UTC') {
$source = new DateTime($this->format('Y-m-d H:i:s.u'), $utcTz);
}
if ($dt->getTimezone()->getName() !== 'UTC') {
$dt = new DateTime($dt->format('Y-m-d H:i:s.u'), $utcTz);
}
$diff = $this->diff($dt ?? static::now($this->tz), $abs);

return $source->diff($dt, $abs);
return $diff->invert ? -$diff->y : $diff->y;
}

/**
* Get the difference in years
* Get the difference in months
*
* @param \Cake\Chronos\ChronosInterface|null $dt The instance to difference from.
* @param bool $abs Get the absolute of the difference
* @return int
*/
public function diffInYears(?ChronosInterface $dt = null, bool $abs = true): int
public function diffInMonths(?ChronosInterface $dt = null, bool $abs = true): int
{
$diff = $this->diff($dt ?? static::now($this->tz), $abs);
$months = $diff->y * ChronosInterface::MONTHS_PER_YEAR + $diff->m;

return $diff->invert ? -$diff->y : $diff->y;
return $diff->invert ? -$months : $months;
}

/**
* Get the difference in months
* Get the difference in months without converting times to UTC which can cause
* a the month to change.
*
* For example, if comparing `2019-06-01 Asia/Tokyo` and `2019-10-01 Asia/Tokyo`,
* the result would be 4 months instead of 3 when using normal `DateTime::diff()`.
*
* @param \Cake\Chronos\ChronosInterface|null $dt The instance to difference from.
* @param bool $abs Get the absolute of the difference
* @return int
*/
public function diffInMonths(?ChronosInterface $dt = null, bool $abs = true): int
public function diffInMonthsIgnoreTimezone(?ChronosInterface $dt = null, bool $abs = true): int
{
$utcTz = new DateTimeZone('UTC');

$source = $this;
if ($this->getTimezone()->getName() !== 'UTC') {
$source = new DateTime($this->format('Y-m-d H:i:s.u'), $utcTz);
}

$dt = $dt ?? static::now($this->tz);
if ($dt->getTimezone()->getName() !== 'UTC') {
$dt = new DateTime($dt->format('Y-m-d H:i:s.u'), $utcTz);
}

$diff = $this->diff($dt ?? static::now($this->tz), $abs);
$months = $diff->y * ChronosInterface::MONTHS_PER_YEAR + $diff->m;

Expand Down
42 changes: 23 additions & 19 deletions tests/TestCase/DateTime/DiffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,6 @@ protected function wrapWithTestNow(Closure $func, $dt = null)
parent::wrapWithTestNow($func, $dt ?? Chronos::createFromDate(2012, 1, 1));
}

/**
* @dataProvider classNameProvider
* @return void
*/
public function testDiffIgnoreTimezone($class)
{
$source = $class::createFromDate(2019, 06, 01, 'Asia/Tokyo');
$target = $class::createFromDate(2019, 10, 01, 'Asia/Tokyo');
$this->assertSame(4, $source->diffIgnoreTimezone($target)->m);

$source = $class::createFromDate(2019, 06, 01, 'UTC');
$target = $class::createFromDate(2019, 10, 01, 'UTC');
$this->assertSame(4, $source->diffIgnoreTimezone($target)->m);

$source = $class::createFromDate(2019, 06, 01, 'UTC');
$target = $class::createFromDate(2019, 10, 01, 'Asia/Tokyo');
$this->assertSame(4, $source->diffIgnoreTimezone($target)->m);
}

/**
* @dataProvider classNameProvider
* @return void
Expand Down Expand Up @@ -147,6 +128,29 @@ public function testDiffInMonthsEnsureIsTruncated($class)
$this->assertSame(1, $dt->diffInMonths($dt->copy()->addMonth()->addDays(16)));
}

/**
* @dataProvider classNameProvider
* @return void
*/
public function testDiffInMonthsIgnoreTimezone($class)
{
$source = $class::createFromDate(2019, 06, 01, 'Asia/Tokyo');
$target = $class::createFromDate(2019, 10, 01, 'Asia/Tokyo');
$this->assertSame(4, $source->diffInMonthsIgnoreTimezone($target));

$source = $class::createFromDate(2019, 06, 01, 'UTC');
$target = $class::createFromDate(2019, 10, 01, 'UTC');
$this->assertSame(4, $source->diffInMonthsIgnoreTimezone($target));

$source = $class::createFromDate(2019, 06, 01, 'UTC');
$target = $class::createFromDate(2019, 10, 01, 'Asia/Tokyo');
$this->assertSame(4, $source->diffInMonthsIgnoreTimezone($target));

$this->wrapWithTestNow(function () use ($class) {
$this->assertSame(1, $class::now()->subMonth()->startOfMonth()->diffInMonthsIgnoreTimezone());
});
}

/**
* @dataProvider classNameProvider
* @return void
Expand Down

0 comments on commit 3f119d1

Please sign in to comment.