Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Impossible CRON expression" for expressions that occur on DST change over #111

Closed
AllenJB opened this issue Mar 28, 2021 · 2 comments
Closed

Comments

@AllenJB
Copy link

AllenJB commented Mar 28, 2021

$expression = "0 1 * * 0";
$cronex = CronExpression::factory($expression);
$tz = new \DateTimeZone("Europe/London");
$dtCurrent = \DateTimeImmutable::createFromFormat("!Y-m-d H:i:s", "2021-03-28 14:55:03", $tz);
$result = $cronex->getPreviousRunDate($dtCurrent, 0, true, $tz->getName());

This case returns an impossible cron expression exception, despite the cron expression being valid and possible.

(For reference Europe/London switched from UTC+0 to UTC+1 at 2021-03-28 01:00:00 UTC )

Performing getNextRunDate using the day before appears to work (may not be what the user might expect, but it's "valid"):

$dtCurrent = \DateTimeImmutable::createFromFormat("!Y-m-d H:i:s", "2021-03-27 14:55:03", $tz);
$result = $cronex->getNextRunDate($dtCurrent, 0, true, $tz->getName());

Returns 4th April

I tested using v3.1.0

@AllenJB
Copy link
Author

AllenJB commented Mar 29, 2021

Just a side note on a workaround for this that I've implemented (I only have to deal with a specific subset of expressions that express a particular time on a particular weekday - month and day of month are always '*' and no '/', ',' or other "special cases" in any given job specification):

If a forward offset transition occurred this week, and the day of week and hour value is the same as the transition, add one hour.

You can use DateTimeZone->getTransitions($timestampLastWeek, $timestampNow) to determine if and when a transition occurred in the past week.

@AllenJB
Copy link
Author

AllenJB commented May 22, 2021

I think this bug may be sensitive to the PHP version being used. 7.4.3 on Windows 10 appeared to not trigger the bug, but 7.4.19 from remi repo on CentOS 7.9 did. After upgrading to PHP 8.0 on Windows 10, the bug now also triggers there.

(There have been no recent changes to the Europe/London timezone as far as I am aware - it doesn't vary its DST changeover dates like some zones do, so I doubt it's a change in timezone definitions)

Scanning the NEWS file in php-src 7.4 branch, the date extension bug fixes in 7.4.5 look like the best candidates for a related change.

Edit: After further testing, I'm now sure the fix for https://bugs.php.net/bug.php?id=79396 broke this library

I set a watch on the datetime value as it's manipulated by the HoursField when attempting to run the above error triggering code.

The result is that the library keeps trying to setTime(1, 59) on the value 2021-03-28T02:59:00+01:00 - it will do this eternally because doing this results in the same value due to the DST forward jump.


I have a draft of a fix for this at https://github.com/AllenJB/cron-expression/tree/dst_change
I'm not quite ready to PR it yet - want to do some more testing tomorrow (for nth != 0, multiple run dates and some non-round-hour-jump timezones) but so far it's passing all tests.


Update: I discovered more fun bugs / unexpected behavior in PHP's DateTime handling. This is taking longer than I hoped. I am persevering tho.

dragonmantank added a commit that referenced this issue Jan 5, 2022
* DST fix (attempt 1)

* More tests and further fixes
WIP as I believe the change to HoursField::isSatisfiedBy is going to break expressions with multiple parts - but one thing at a time!

* WIP More tests and further fixes;
API change: isSatisfiedBy now requires knowledge of which direction we're travelling in to handle checking initial values correctly

* WIP Save Point - Trying to fix one thing breaks another, but I have an idea...

* WIP Abstracted NextRunDateTime which keeps track of offset changes, regardless of whether they occurred when changing minute or hour (and to persist them until a next change is made)

* WIP Fix easy fixes

* WIP More test fixes

* All current tests pass!

* Fix for issue #112;
Use a cache of timezone transitions to avoid having to modify date/time objects every single time we want to check if hour is satisfied
All tests pass

* Cleanup
All tests pass

* Cleanup
All tests pass

* Cleanup - backing out the NextRunDateTime abstraction;
All tests pass

* Cleanup;
All tests pass

* Cleanup (diff tidy, restoring deleted tests);
All tests pass

* Cleanup (diff tidy);
All tests pass

* Fix CI issues

* Fix CI issues

Co-authored-by: Chris Tankersley <chris@ctankersley.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant