From 61fde5ed4245277c57c93dbf4df9302286098c85 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Sun, 7 Jan 2024 16:19:29 +0200 Subject: [PATCH] scheduled jobs timezone --- .../Espo/Core/Job/ConfigDataProvider.php | 9 ++++ .../Espo/Core/Job/ScheduleProcessor.php | 11 ++++- .../Espo/Resources/defaults/config.php | 1 + .../Espo/Resources/defaults/systemConfig.php | 1 + .../Espo/Resources/i18n/en_US/Settings.json | 2 + .../layouts/Settings/jobsSettings.json | 5 ++ .../metadata/entityDefs/Settings.json | 4 ++ upgrades/8.2/data.json | 5 ++ upgrades/8.2/scripts/AfterUpgrade.php | 48 +++++++++++++++++++ 9 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 upgrades/8.2/data.json create mode 100644 upgrades/8.2/scripts/AfterUpgrade.php diff --git a/application/Espo/Core/Job/ConfigDataProvider.php b/application/Espo/Core/Job/ConfigDataProvider.php index a7d3052c6a..1f60c9459d 100644 --- a/application/Espo/Core/Job/ConfigDataProvider.php +++ b/application/Espo/Core/Job/ConfigDataProvider.php @@ -54,4 +54,13 @@ class ConfigDataProvider { return (bool) $this->config->get('jobNoTableLocking'); } + + public function getTimeZone(): string + { + if ($this->config->get('jobForceUtc')) { + return 'UTC'; + } + + return $this->config->get('timeZone') ?? 'UTC'; + } } diff --git a/application/Espo/Core/Job/ScheduleProcessor.php b/application/Espo/Core/Job/ScheduleProcessor.php index 3fd1b3e67b..7f24093907 100644 --- a/application/Espo/Core/Job/ScheduleProcessor.php +++ b/application/Espo/Core/Job/ScheduleProcessor.php @@ -29,6 +29,7 @@ namespace Espo\Core\Job; +use DateTimeZone; use Espo\Core\Job\Preparator\Data as PreparatorData; use Espo\Core\ORM\EntityManager; use Espo\Core\Utils\DateTime as DateTimeUtil; @@ -64,7 +65,8 @@ class ScheduleProcessor private QueueUtil $queueUtil, private ScheduleUtil $scheduleUtil, private PreparatorFactory $preparatorFactory, - private MetadataProvider $metadataProvider + private MetadataProvider $metadataProvider, + private ConfigDataProvider $configDataProvider ) {} public function process(): void @@ -172,13 +174,18 @@ class ScheduleProcessor return null; } + $timeZone = $this->configDataProvider->getTimeZone(); + try { - return $cronExpression->getNextRunDate()->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT); + $next = $cronExpression->getNextRunDate(timeZone: $timeZone) + ->setTimezone(new DateTimeZone('UTC')); } catch (Exception) { $this->log->error("Scheduled Job '$id': Unsupported scheduling expression '$scheduling'."); return null; } + + return $next->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT); } } diff --git a/application/Espo/Resources/defaults/config.php b/application/Espo/Resources/defaults/config.php index c20c905004..a397e85854 100644 --- a/application/Espo/Resources/defaults/config.php +++ b/application/Espo/Resources/defaults/config.php @@ -48,6 +48,7 @@ return [ /** Interval between process runs in seconds. */ 'daemonInterval' => 10, 'daemonProcessTimeout' => 36000, + 'jobForceUtc' => false, 'recordsPerPage' => 20, 'recordsPerPageSmall' => 5, 'recordsPerPageSelect' => 10, diff --git a/application/Espo/Resources/defaults/systemConfig.php b/application/Espo/Resources/defaults/systemConfig.php index 161bcb6875..22325dbdf8 100644 --- a/application/Espo/Resources/defaults/systemConfig.php +++ b/application/Espo/Resources/defaults/systemConfig.php @@ -126,6 +126,7 @@ return [ 'jobRunInParallel', 'jobPoolConcurrencyNumber', 'jobPeriodForActiveProcess', + 'jobForceUtc', 'cronMinInterval', 'daemonInterval', 'daemonProcessTimeout', diff --git a/application/Espo/Resources/i18n/en_US/Settings.json b/application/Espo/Resources/i18n/en_US/Settings.json index 7253f4a9d5..cf3e6bfdb3 100644 --- a/application/Espo/Resources/i18n/en_US/Settings.json +++ b/application/Espo/Resources/i18n/en_US/Settings.json @@ -127,6 +127,7 @@ "jobRunInParallel": "Jobs Run in Parallel", "jobMaxPortion": "Jobs Max Portion", "jobPoolConcurrencyNumber": "Jobs Pool Concurrency Number", + "jobForceUtc": "Force UTC Time Zone", "daemonInterval": "Daemon Interval", "daemonMaxProcessNumber": "Daemon Max Process Number", "daemonProcessTimeout": "Daemon Process Timeout", @@ -262,6 +263,7 @@ "jobRunInParallel": "Jobs will be executed in parallel processes.", "jobPoolConcurrencyNumber": "Max number of processes run simultaneously.", "jobMaxPortion": "Max number of jobs processed per one execution.", + "jobForceUtc": "Use the UTC time zone for scheduled jobs. Otherwise, the time zone set in settings will be used.", "daemonInterval": "Interval between process cron runs in seconds.", "daemonMaxProcessNumber": "Max number of cron processes run simultaneously.", "daemonProcessTimeout": "Max execution time (in seconds) allocated for a single cron process.", diff --git a/application/Espo/Resources/layouts/Settings/jobsSettings.json b/application/Espo/Resources/layouts/Settings/jobsSettings.json index 254f7a71d7..2d689e3fb0 100644 --- a/application/Espo/Resources/layouts/Settings/jobsSettings.json +++ b/application/Espo/Resources/layouts/Settings/jobsSettings.json @@ -10,5 +10,10 @@ [{"name": "daemonInterval"}, {"name": "daemonMaxProcessNumber"}], [{"name": "daemonProcessTimeout"}, false] ] + }, + { + "rows": [ + [{"name": "jobForceUtc"}, false] + ] } ] diff --git a/application/Espo/Resources/metadata/entityDefs/Settings.json b/application/Espo/Resources/metadata/entityDefs/Settings.json index 2092481011..7d3e711ec7 100644 --- a/application/Espo/Resources/metadata/entityDefs/Settings.json +++ b/application/Espo/Resources/metadata/entityDefs/Settings.json @@ -748,6 +748,10 @@ "tooltip": true, "min": 1 }, + "jobForceUtc": { + "type": "bool", + "tooltip": true + }, "daemonInterval": { "type": "int", "tooltip": true diff --git a/upgrades/8.2/data.json b/upgrades/8.2/data.json new file mode 100644 index 0000000000..d64e496d57 --- /dev/null +++ b/upgrades/8.2/data.json @@ -0,0 +1,5 @@ +{ + "manifest": { + "delete": [] + } +} diff --git a/upgrades/8.2/scripts/AfterUpgrade.php b/upgrades/8.2/scripts/AfterUpgrade.php new file mode 100644 index 0000000000..c5bd91c257 --- /dev/null +++ b/upgrades/8.2/scripts/AfterUpgrade.php @@ -0,0 +1,48 @@ +. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU Affero General Public License version 3. + * + * In accordance with Section 7(b) of the GNU Affero General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +use Espo\Core\Container; +use Espo\Core\InjectableFactory; +use Espo\Core\Utils\Config\ConfigWriter; + +/** @noinspection PhpMultipleClassDeclarationsInspection */ +class AfterUpgrade +{ + public function run(Container $container): void + { + $configWriter = $container->getByClass(InjectableFactory::class) + ->create(ConfigWriter::class); + + $configWriter->setMultiple([ + 'jobForceUtc' => true, + ]); + + $configWriter->save(); + } +}