Skip to content

Commit

Permalink
feature #49739 [Scheduler] Add DateIntervalTrigger and DatePeriodTrig…
Browse files Browse the repository at this point in the history
…ger (fabpot)

This PR was merged into the 6.3 branch.

Discussion
----------

[Scheduler] Add DateIntervalTrigger and DatePeriodTrigger

| Q             | A
| ------------- | ---
| Branch?       | 6.3
| Bug fix?      | no
| New feature?  | yes <!-- please update src/**/CHANGELOG.md files -->
| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tickets       | n/a
| License       | MIT
| Doc PR        | -

The `PeriodicalTrigger` should only be used for a fixed period of time that can be converted to a number of seconds.
Using it for `DatePeriod` does not make sense as the number of seconds between 2 runs can differ.
This PR introduces 2 new triggers that behave correctly in such cases.

Commits
-------

9a4ee58 [Scheduler] Add DateIntervalTrigger and DatePeriodTrigger
  • Loading branch information
fabpot committed Mar 20, 2023
2 parents b885d0d + 9a4ee58 commit 8b8045b
Show file tree
Hide file tree
Showing 8 changed files with 341 additions and 254 deletions.
6 changes: 3 additions & 3 deletions src/Symfony/Component/Scheduler/RecurringMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

use Symfony\Component\Scheduler\Exception\InvalidArgumentException;
use Symfony\Component\Scheduler\Trigger\CronExpressionTrigger;
use Symfony\Component\Scheduler\Trigger\PeriodicalTrigger;
use Symfony\Component\Scheduler\Trigger\DateIntervalTrigger;
use Symfony\Component\Scheduler\Trigger\TriggerInterface;

/**
Expand All @@ -32,13 +32,13 @@ private function __construct(
*
* @see https://php.net/datetime.formats.relative
*/
public static function every(string $frequency, object $message, \DateTimeImmutable $from = new \DateTimeImmutable(), \DateTimeImmutable $until = new \DateTimeImmutable('3000-01-01')): self
public static function every(string $frequency, object $message, string|\DateTimeImmutable $from = new \DateTimeImmutable(), string|\DateTimeImmutable $until = new \DateTimeImmutable('3000-01-01')): self
{
if (false === $interval = \DateInterval::createFromDateString($frequency)) {
throw new InvalidArgumentException(sprintf('Frequency "%s" cannot be parsed.', $frequency));
}

return new self(PeriodicalTrigger::create($interval, $from, $until), $message);
return new self(new DateIntervalTrigger($interval, $from, $until), $message);
}

public static function cron(string $expression, object $message): self
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Scheduler\Tests\Trigger;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Scheduler\Trigger\DatePeriodTrigger;
use Symfony\Component\Scheduler\Trigger\TriggerInterface;

abstract class AbstractTriggerTest extends TestCase
{
/**
* @dataProvider providerGetNextRunDate
*/
public function testGetNextRunDate(TriggerInterface $trigger, array $expected)
{
$this->assertEquals($expected, $this->getNextRunDates($trigger));
}

abstract public static function providerGetNextRunDate(): iterable;

protected static function createTrigger(string $interval): DatePeriodTrigger
{
return new DatePeriodTrigger(
new \DatePeriod(new \DateTimeImmutable('13:45'), \DateInterval::createFromDateString($interval), new \DateTimeImmutable('2023-06-19'))
);
}

private function getNextRunDates(TriggerInterface $trigger): array
{
$dates = [];
$i = 0;
$next = new \DateTimeImmutable();
while ($i++ < 20) {
$next = $trigger->getNextRunDate($next);
if (!$next) {
break;
}

$dates[] = $next;
}

return $dates;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Scheduler\Tests\Trigger;

use Symfony\Component\Scheduler\Exception\InvalidArgumentException;
use Symfony\Component\Scheduler\Trigger\DateIntervalTrigger;
use Symfony\Component\Scheduler\Trigger\DatePeriodTrigger;

class DateIntervalTriggerTest extends DatePeriodTriggerTest
{
/**
* @dataProvider provideForConstructor
*/
public function testConstructor(DateIntervalTrigger $trigger)
{
$run = new \DateTimeImmutable('2222-02-22 13:34:00');

$this->assertSame('2222-02-23 13:34:00', $trigger->getNextRunDate($run)->format('Y-m-d H:i:s'));
}

public static function provideForConstructor(): iterable
{
$from = new \DateTimeImmutable($now = '2222-02-22 13:34:00');
$until = new \DateTimeImmutable($farFuture = '3000-01-01');
$day = new \DateInterval('P1D');

return [
[new DateIntervalTrigger(86400, $from, $until)],
[new DateIntervalTrigger('86400', $from, $until)],
[new DateIntervalTrigger('P1D', $from, $until)],
[new DateIntervalTrigger($day, $now, $farFuture)],
[new DateIntervalTrigger($day, $now)],
];
}

/**
* @dataProvider getInvalidIntervals
*/
public function testInvalidInterval($interval)
{
$this->expectException(InvalidArgumentException::class);

new DateIntervalTrigger($interval, $now = new \DateTimeImmutable(), $now->modify('1 day'));
}

public static function getInvalidIntervals(): iterable
{
yield ['wrong'];
yield ['3600.5'];
yield [-3600];
}

/**
* @dataProvider providerGetNextRunDateAgain
*/
public function testGetNextRunDateAgain(DateIntervalTrigger $trigger, \DateTimeImmutable $lastRun, ?\DateTimeImmutable $expected)
{
$this->assertEquals($expected, $trigger->getNextRunDate($lastRun));
}

public static function providerGetNextRunDateAgain(): iterable
{
$trigger = new DateIntervalTrigger(
600,
new \DateTimeImmutable('2020-02-20T02:00:00+02'),
new \DateTimeImmutable('2020-02-20T03:00:00+02')
);

yield [
$trigger,
new \DateTimeImmutable('@0'),
new \DateTimeImmutable('2020-02-20T02:00:00+02'),
];
yield [
$trigger,
new \DateTimeImmutable('2020-02-20T01:59:59.999999+02'),
new \DateTimeImmutable('2020-02-20T02:00:00+02'),
];
yield [
$trigger,
new \DateTimeImmutable('2020-02-20T02:00:00+02'),
new \DateTimeImmutable('2020-02-20T02:10:00+02'),
];
yield [
$trigger,
new \DateTimeImmutable('2020-02-20T02:05:00+02'),
new \DateTimeImmutable('2020-02-20T02:10:00+02'),
];
yield [
$trigger,
new \DateTimeImmutable('2020-02-20T02:49:59.999999+02'),
new \DateTimeImmutable('2020-02-20T02:50:00+02'),
];
yield [
$trigger,
new \DateTimeImmutable('2020-02-20T02:50:00+02'),
null,
];
yield [
$trigger,
new \DateTimeImmutable('2020-02-20T03:00:00+02'),
null,
];

$trigger = new DateIntervalTrigger(
600,
new \DateTimeImmutable('2020-02-20T02:00:00Z'),
new \DateTimeImmutable('2020-02-20T03:01:00Z')
);

yield [
$trigger,
new \DateTimeImmutable('2020-02-20T02:59:59.999999Z'),
new \DateTimeImmutable('2020-02-20T03:00:00Z'),
];
yield [
$trigger,
new \DateTimeImmutable('2020-02-20T03:00:00Z'),
null,
];
}

protected static function createTrigger(string $interval): DatePeriodTrigger
{
return new DateIntervalTrigger($interval, '2023-03-19 13:45', '2023-06-19');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Scheduler\Tests\Trigger;

use Symfony\Component\Scheduler\Trigger\DatePeriodTrigger;

class DatePeriodTriggerTest extends AbstractTriggerTest
{
public static function providerGetNextRunDate(): iterable
{
yield [
self::createTrigger('next tuesday'),
[
new \DateTimeImmutable('2023-03-21 13:45:00'),
new \DateTimeImmutable('2023-03-28 13:45:00'),
new \DateTimeImmutable('2023-04-04 13:45:00'),
new \DateTimeImmutable('2023-04-11 13:45:00'),
new \DateTimeImmutable('2023-04-18 13:45:00'),
new \DateTimeImmutable('2023-04-25 13:45:00'),
new \DateTimeImmutable('2023-05-02 13:45:00'),
new \DateTimeImmutable('2023-05-09 13:45:00'),
new \DateTimeImmutable('2023-05-16 13:45:00'),
new \DateTimeImmutable('2023-05-23 13:45:00'),
new \DateTimeImmutable('2023-05-30 13:45:00'),
new \DateTimeImmutable('2023-06-06 13:45:00'),
new \DateTimeImmutable('2023-06-13 13:45:00'),
],
];

yield [
self::createTrigger('last day of next month'),
[
new \DateTimeImmutable('2023-04-30 13:45:00'),
new \DateTimeImmutable('2023-05-31 13:45:00'),
],
];

yield [
self::createTrigger('first monday of next month'),
[
new \DateTimeImmutable('2023-04-03 13:45:00'),
new \DateTimeImmutable('2023-05-01 13:45:00'),
new \DateTimeImmutable('2023-06-05 13:45:00'),
],
];
}

protected static function createTrigger(string $interval): DatePeriodTrigger
{
return new DatePeriodTrigger(
new \DatePeriod(new \DateTimeImmutable('2023-03-19 13:45'), \DateInterval::createFromDateString($interval), new \DateTimeImmutable('2023-06-19')),
);
}
}

0 comments on commit 8b8045b

Please sign in to comment.