mtdowling / cron-expression

CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due
http://mtdowling.com/blog/2012/06/03/cron-expressions-in-php/
MIT License
4.9k stars 335 forks source link

Inconsistent behavior with regards to timezones (2.0) #164

Closed AllenJB closed 6 years ago

AllenJB commented 6 years ago

I'm using version 2.0 from the dragonmantank repository, but issues are not enabled there, so I'm submitting this here. The issue may also be present in the mtdowling repository / older versions - I haven't tested.

The cron expression timezone is not being applied as expected, specifically when the currentTime parameter is passed as either a DateTime or DateTimeImmutable object.

This can be worked around by either passing the currentTime parameter as an (ISO) string or unix timestamp, or by changing the timezone of the currentTime parameter to match the cron timezone.

I demonstrate this below using the getPreviousRunDate method, but I suspect many, if not all, methods are affected due to the way they ultimately use getRunDate.

Code:

// Run at 7 am every day in America/New_York timezone
$cron = CronExpression::factory("0 7 * * *");
$tzCron = "America/New_York";
$tzServer = new \DateTimeZone("Europe/London");

$dtCurrent = \DateTime::createFromFormat("!Y-m-d H:i:s", "2017-10-17 10:00:00", $tzServer);
$dtPrev = $cron->getPreviousRunDate($dtCurrent, 0, true, $tzCron);
print "DateTime: ". $dtPrev->format("U \: c \: e") ."\n\n";

$dtCurrent = \DateTimeImmutable::createFromFormat("!Y-m-d H:i:s", "2017-10-17 10:00:00", $tzServer);
$dtPrev = $cron->getPreviousRunDate($dtCurrent, 0, true, $tzCron);
print "DateTimeImmutable: ". $dtPrev->format("U \: c \: e") ."\n\n";

$dtCurrent = \DateTimeImmutable::createFromFormat("!Y-m-d H:i:s", "2017-10-17 10:00:00", $tzServer);
$dtPrev = $cron->getPreviousRunDate($dtCurrent->format("c"), 0, true, $tzCron);
print "ISO String: ". $dtPrev->format("U \: c \: e") ."\n\n";

$dtCurrent = \DateTimeImmutable::createFromFormat("!Y-m-d H:i:s", "2017-10-17 10:00:00", $tzServer);
$dtPrev = $cron->getPreviousRunDate($dtCurrent->format("\@U"), 0, true, $tzCron);
print "Timestamp: ". $dtPrev->format("U \: c \: e") ."\n\n";

Actual Result:

DateTime: 1508220000 : 2017-10-17T07:00:00+01:00 : Europe/London

DateTimeImmutable: 1508220000 : 2017-10-17T07:00:00+01:00 : Europe/London

ISO String: 1508151600 : 2017-10-16T07:00:00-04:00 : America/New_York

Timestamp: 1508151600 : 2017-10-16T07:00:00-04:00 : America/New_York

Expected Result:

DateTime: 1508151600 : 2017-10-16T07:00:00-04:00 : America/New_York

DateTimeImmutable: 1508151600 : 2017-10-16T07:00:00-04:00 : America/New_York

ISO String: 1508151600 : 2017-10-16T07:00:00-04:00 : America/New_York

Timestamp: 1508151600 : 2017-10-16T07:00:00-04:00 : America/New_York

(or equivalent - the return value might retain the timezone of the currentTime parameter (but should still be the equivalent time to 7am America/New_York), but since DateTime[Immutable] objects can be compared without regard for the timezone, I don't think it matters too much)

laurencei commented 6 years ago

@AllenJB - can you checkout PR https://github.com/dragonmantank/cron-expression/pull/3

Can we close the issue here, and any further discussions in that PR. Thanks.

dragonmantank commented 6 years ago

This was merged in as https://github.com/dragonmantank/cron-expression/pull/3 , closing here.