Skip to content

Commit a0cce9a

Browse files
author
CipherdevNL
committed
fix: skipped run after daylight saving turnover point
1 parent f37e405 commit a0cce9a

File tree

2 files changed

+77
-17
lines changed

2 files changed

+77
-17
lines changed

src/Cron/HoursField.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public function increment(DateTimeInterface &$date, $invert = false, $parts = nu
167167
if (! $invert) {
168168
if ($originalHour >= $target) {
169169
$distance = 24 - $originalHour;
170-
$date = $this->timezoneSafeModify($date, "+{$distance} hours");
170+
$date = $date->modify("+{$distance} hours");
171171

172172
$actualDay = (int)$date->format('d');
173173
$actualHour = (int)$date->format('H');
@@ -180,7 +180,7 @@ public function increment(DateTimeInterface &$date, $invert = false, $parts = nu
180180
}
181181

182182
$distance = $target - $originalHour;
183-
$date = $this->timezoneSafeModify($date, "+{$distance} hours");
183+
$date = $date->modify("+{$distance} hours");
184184
} else {
185185
if ($originalHour <= $target) {
186186
$distance = ($originalHour + 1);

tests/Cron/DaylightSavingsTest.php

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -209,28 +209,89 @@ public function testOffsetDecrementsPreviousRunDate(): void
209209
$this->assertEquals($dtExpected, $cron->getPreviousRunDate($dtCurrent, 0, false, $tz->getName()));
210210
}
211211

212-
public function testOffsetIncrementsMultipleRunDates(): void
212+
public static function dayLightSavingExamples(): \Generator
213213
{
214-
$expression = "0 1 * * 0";
215-
$cron = new CronExpression($expression);
216-
$tz = new \DateTimeZone("Europe/London");
214+
yield 'fixed hour before turnover point' => [
215+
"0 1 * * 0",
216+
[
217+
"2021-03-14 01:00+00:00",
218+
"2021-03-21 01:00+00:00",
219+
"2021-03-28 02:00+01:00",
220+
"2021-04-04 01:00+01:00",
221+
"2021-04-11 01:00+01:00",
222+
],
223+
new \DateTimeZone("Europe/London"),
224+
"2021-03-13 00:00+00:00",
225+
"2021-04-12 00:00+01:00",
226+
];
217227

218-
$expected = [
219-
$this->createDateTimeExactly("2021-03-14 01:00+00:00", $tz),
220-
$this->createDateTimeExactly("2021-03-21 01:00+00:00", $tz),
221-
$this->createDateTimeExactly("2021-03-28 02:00+01:00", $tz),
222-
$this->createDateTimeExactly("2021-04-04 01:00+01:00", $tz),
223-
$this->createDateTimeExactly("2021-04-11 01:00+01:00", $tz),
228+
yield 'fixed hour after turnover point #202' => [
229+
"0 10 * * *",
230+
[
231+
"2025-03-08 10:00-06:00",
232+
"2025-03-09 10:00-05:00",
233+
"2025-03-10 10:00-05:00",
234+
],
235+
new \DateTimeZone("America/Chicago"),
236+
"2025-03-08 09:00-06:00",
237+
"2025-03-10 11:00-05:00",
224238
];
225239

226-
$dtCurrent = $this->createDateTimeExactly("2021-03-13 00:00+00:00", $tz);
227-
$actual = $cron->getMultipleRunDates(5, $dtCurrent, false, true, $tz->getName());
240+
yield 'fixed hour after turnover point #154' => [
241+
"30 07 * * *",
242+
[
243+
"2023-03-11 07:30-05:00",
244+
"2023-03-12 07:30-04:00",
245+
"2023-03-13 07:30-04:00",
246+
],
247+
new \DateTimeZone("America/New_York"),
248+
"2023-03-10 08:00-05:00",
249+
"2023-03-13 08:00-04:00",
250+
];
251+
252+
yield 'fixed time inside the daylight transition hour' => [
253+
"30 2 * * *",
254+
[
255+
"2025-03-08 02:30-06:00",
256+
"2025-03-09 03:30-05:00",
257+
"2025-03-10 02:30-05:00",
258+
],
259+
new \DateTimeZone("America/Chicago"),
260+
"2025-03-08 01:30-06:00",
261+
"2025-03-10 11:00-05:00",
262+
];
263+
}
264+
265+
/**
266+
* @param string[] $expected
267+
*
268+
* @dataProvider dayLightSavingExamples
269+
*/
270+
public function testOffsetIncrementsMultipleRunDates(
271+
string $expression,
272+
array $expected,
273+
\DateTimeZone $tz,
274+
string $currentDate,
275+
string $currentInvertedDate
276+
): void {
277+
$cron = new CronExpression($expression);
278+
279+
$expected = array_map(
280+
function (string $dtString) use ($tz) {
281+
return $this->createDateTimeExactly($dtString, $tz);
282+
},
283+
$expected
284+
);
285+
$total = count($expected);
286+
287+
$dtCurrent = $this->createDateTimeExactly($currentDate, $tz);
288+
$actual = $cron->getMultipleRunDates($total, $dtCurrent, false, true, $tz->getName());
228289
foreach ($expected as $dtExpected) {
229290
$this->assertContainsEquals($dtExpected, $actual);
230291
}
231292

232-
$dtCurrent = $this->createDateTimeExactly("2021-04-12 00:00+01:00", $tz);
233-
$actual = $cron->getMultipleRunDates(5, $dtCurrent, true, true, $tz->getName());
293+
$dtCurrent = $this->createDateTimeExactly($currentInvertedDate, $tz);
294+
$actual = $cron->getMultipleRunDates($total, $dtCurrent, true, true, $tz->getName());
234295
foreach ($expected as $dtExpected) {
235296
$this->assertContainsEquals($dtExpected, $actual);
236297
}
@@ -342,7 +403,6 @@ public function testOffsetDecrementsEveryOtherHour(): void
342403
$expected = [
343404
$this->createDateTimeExactly("2020-10-24 22:00+01:00", $tz),
344405
$this->createDateTimeExactly("2020-10-25 00:00+01:00", $tz),
345-
$this->createDateTimeExactly("2020-10-25 01:00+00:00", $tz),
346406
$this->createDateTimeExactly("2020-10-25 02:00+00:00", $tz),
347407
$this->createDateTimeExactly("2020-10-25 04:00+00:00", $tz),
348408
];

0 commit comments

Comments
 (0)