diff --git a/src/Symfony/Component/Scheduler/CHANGELOG.md b/src/Symfony/Component/Scheduler/CHANGELOG.md index f5a3d015eac6..a2b7fd057ed1 100644 --- a/src/Symfony/Component/Scheduler/CHANGELOG.md +++ b/src/Symfony/Component/Scheduler/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.4 +--- + + * Allow setting timezone of next run date in CronExpressionTrigger + 6.3 --- diff --git a/src/Symfony/Component/Scheduler/RecurringMessage.php b/src/Symfony/Component/Scheduler/RecurringMessage.php index ed0badb8503d..d99ec2eb1695 100644 --- a/src/Symfony/Component/Scheduler/RecurringMessage.php +++ b/src/Symfony/Component/Scheduler/RecurringMessage.php @@ -48,17 +48,17 @@ public static function every(string|int|\DateInterval $frequency, object $messag return new self(new PeriodicalTrigger($frequency, $from, $until), $message); } - public static function cron(string $expression, object $message): self + public static function cron(string $expression, object $message, \DateTimeZone|string $timezone = null): self { if (!str_contains($expression, '#')) { - return new self(CronExpressionTrigger::fromSpec($expression), $message); + return new self(CronExpressionTrigger::fromSpec($expression, null, $timezone), $message); } if (!$message instanceof \Stringable) { throw new InvalidArgumentException('A message must be stringable to use "hashed" cron expressions.'); } - return new self(CronExpressionTrigger::fromSpec($expression, (string) $message), $message); + return new self(CronExpressionTrigger::fromSpec($expression, (string) $message, $timezone), $message); } public static function trigger(TriggerInterface $trigger, object $message): self diff --git a/src/Symfony/Component/Scheduler/Tests/Trigger/CronExpressionTriggerTest.php b/src/Symfony/Component/Scheduler/Tests/Trigger/CronExpressionTriggerTest.php index db21020c61e0..cf12a7ceccf5 100644 --- a/src/Symfony/Component/Scheduler/Tests/Trigger/CronExpressionTriggerTest.php +++ b/src/Symfony/Component/Scheduler/Tests/Trigger/CronExpressionTriggerTest.php @@ -96,4 +96,19 @@ public function testFromHashWithStandardExpression() $this->assertSame('56 20 1 9 0', (string) CronExpressionTrigger::fromSpec('56 20 1 9 0', 'some context')); $this->assertSame('0 0 * * *', (string) CronExpressionTrigger::fromSpec('@daily')); } + + public function testDefaultTimezone() + { + $now = new \DateTimeImmutable('Europe/Paris'); + $trigger = CronExpressionTrigger::fromSpec('0 12 * * *'); + $this->assertSame('Europe/Paris', $trigger->getNextRunDate($now)->getTimezone()->getName()); + } + + public function testTimezoneIsUsed() + { + $now = new \DateTimeImmutable('Europe/Paris'); + $timezone = new \DateTimeZone('UTC'); + $trigger = CronExpressionTrigger::fromSpec('0 12 * * *', null, $timezone); + $this->assertSame('UTC', $trigger->getNextRunDate($now)->getTimezone()->getName()); + } } diff --git a/src/Symfony/Component/Scheduler/Trigger/CronExpressionTrigger.php b/src/Symfony/Component/Scheduler/Trigger/CronExpressionTrigger.php index 169af5b15ee4..20fcc960be36 100644 --- a/src/Symfony/Component/Scheduler/Trigger/CronExpressionTrigger.php +++ b/src/Symfony/Component/Scheduler/Trigger/CronExpressionTrigger.php @@ -46,9 +46,13 @@ final class CronExpressionTrigger implements TriggerInterface [0, 6], ]; + private readonly ?string $timezone; + public function __construct( private readonly CronExpression $expression = new CronExpression('* * * * *'), + \DateTimeZone|string $timezone = null, ) { + $this->timezone = $timezone instanceof \DateTimeZone ? $timezone->getName() : $timezone; } public function __toString(): string @@ -56,26 +60,26 @@ public function __toString(): string return $this->expression->getExpression(); } - public static function fromSpec(string $expression = '* * * * *', string $context = null): self + public static function fromSpec(string $expression = '* * * * *', string $context = null, \DateTimeZone|string $timezone = null): self { if (!class_exists(CronExpression::class)) { throw new LogicException(sprintf('You cannot use "%s" as the "cron expression" package is not installed. Try running "composer require dragonmantank/cron-expression".', __CLASS__)); } if (!str_contains($expression, '#')) { - return new self(new CronExpression($expression)); + return new self(new CronExpression($expression), $timezone); } if (null === $context) { throw new LogicException('A context must be provided to use "hashed" cron expressions.'); } - return new self(new CronExpression(self::parseHashed($expression, $context))); + return new self(new CronExpression(self::parseHashed($expression, $context)), $timezone); } public function getNextRunDate(\DateTimeImmutable $run): ?\DateTimeImmutable { - return \DateTimeImmutable::createFromInterface($this->expression->getNextRunDate($run)); + return \DateTimeImmutable::createFromInterface($this->expression->getNextRunDate($run, timeZone: $this->timezone)); } private static function parseHashed(string $expression, string $context): string