From 21908732c607aa924208d57ae128ea6d0822a90f Mon Sep 17 00:00:00 2001 From: "Patrik J. Braun" Date: Fri, 21 May 2021 16:38:29 +0200 Subject: [PATCH] Fixing periodic job scheduling timezone issue. #273 Note: current time format change from valid timestamp to hours*60+minutes, current config wont be valid. --- src/backend/model/jobs/JobManager.ts | 6 +++++ src/common/config/private/PrivateConfig.ts | 6 ++--- src/common/entities/job/JobScheduleDTO.ts | 25 +++++++++---------- .../jobs/jobs.settings.component.html | 6 ++--- .../settings/jobs/jobs.settings.component.ts | 16 +++++++++--- .../timepicker.component.ts | 17 ++++++++++--- 6 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/backend/model/jobs/JobManager.ts b/src/backend/model/jobs/JobManager.ts index 0933e3acf..232295d83 100644 --- a/src/backend/model/jobs/JobManager.ts +++ b/src/backend/model/jobs/JobManager.ts @@ -90,6 +90,9 @@ export class JobManager implements IJobManager, IJobListener { this.timers = []; } + /** + * Schedules all jobs to run + */ public runSchedules(): void { this.stopSchedules(); Logger.info(LOG_TAG, 'Running job schedules'); @@ -100,6 +103,9 @@ export class JobManager implements IJobManager, IJobListener { return this.getAvailableJobs().find((t): boolean => t.Name === jobName); } + /** + * Schedules a single job to run + */ private runSchedule(schedule: JobScheduleDTO): void { const nextDate = JobScheduleDTOUtils.getNextRunningDate(new Date(), schedule); if (nextDate && nextDate.getTime() > Date.now()) { diff --git a/src/common/config/private/PrivateConfig.ts b/src/common/config/private/PrivateConfig.ts index 98f21dbd7..2c445f8cb 100644 --- a/src/common/config/private/PrivateConfig.ts +++ b/src/common/config/private/PrivateConfig.ts @@ -144,7 +144,7 @@ export class ScheduledJobTrigger implements JobTrigger { @ConfigProperty({type: JobTriggerType}) readonly type = JobTriggerType.scheduled; - @ConfigProperty() + @ConfigProperty({type: 'unsignedInt'}) time: number; // data time } @@ -152,9 +152,9 @@ export class ScheduledJobTrigger implements JobTrigger { export class PeriodicJobTrigger implements JobTrigger { @ConfigProperty({type: JobTriggerType}) readonly type = JobTriggerType.periodic; - @ConfigProperty() + @ConfigProperty({type: 'unsignedInt', max: 7}) periodicity: number; // 0-6: week days 7 every day - @ConfigProperty() + @ConfigProperty({type: 'unsignedInt', max: 23 * 60 + 59}) atTime: number; // day time } diff --git a/src/common/entities/job/JobScheduleDTO.ts b/src/common/entities/job/JobScheduleDTO.ts index ce30e0e6c..a4c391e68 100644 --- a/src/common/entities/job/JobScheduleDTO.ts +++ b/src/common/entities/job/JobScheduleDTO.ts @@ -18,7 +18,7 @@ export interface ScheduledJobTrigger extends JobTrigger { export interface PeriodicJobTrigger extends JobTrigger { type: JobTriggerType.periodic; periodicity: number; // 0-6: week days 7 every day - atTime: number; // day time + atTime: number; // day time min value: 0, max: 23*60+59 } export interface AfterJobTrigger extends JobTrigger { @@ -37,26 +37,26 @@ export interface JobScheduleDTO { export const JobScheduleDTOUtils = { - getNextDayOfTheWeek: (refDate: Date, dayOfWeek: number) => { + getNextDayOfTheWeek: (refDate: Date, dayOfWeek: number): Date => { const date = new Date(refDate); date.setDate(refDate.getDate() + (dayOfWeek + 1 + 7 - refDate.getDay()) % 7); if (date.getDay() === refDate.getDay()) { return new Date(refDate); } - date.setHours(0, 0, 0, 0); + date.setUTCHours(0, 0, 0, 0); return date; }, nextValidDate: (date: Date, h: number, m: number, dayDiff: number): Date => { - - date.setSeconds(0); - if (date.getHours() < h || (date.getHours() === h && date.getMinutes() < m)) { - date.setHours(h); - date.setMinutes(m); + date.setUTCSeconds(0); + date.setUTCMilliseconds(0); + if (date.getUTCHours() < h || (date.getUTCHours() === h && date.getUTCMinutes() < m)) { + date.setUTCHours(h); + date.setUTCMinutes(m); } else { date.setTime(date.getTime() + dayDiff); - date.setHours(h); - date.setMinutes(m); + date.setUTCHours(h); + date.setUTCMinutes(m); } return date; }, @@ -68,9 +68,8 @@ export const JobScheduleDTOUtils = { case JobTriggerType.periodic: - - const hour = Math.floor(schedule.trigger.atTime / 1000 / (60 * 60)); - const minute = (schedule.trigger.atTime / 1000 / 60) % 60; + const hour = Math.min(23, Math.floor(schedule.trigger.atTime / 60)); + const minute = schedule.trigger.atTime % 60; if (schedule.trigger.periodicity <= 6) { // Between Monday and Sunday const nextRunDate = JobScheduleDTOUtils.getNextDayOfTheWeek(refDate, schedule.trigger.periodicity); diff --git a/src/frontend/app/ui/settings/jobs/jobs.settings.component.html b/src/frontend/app/ui/settings/jobs/jobs.settings.component.html index 977eadafc..a93729618 100644 --- a/src/frontend/app/ui/settings/jobs/jobs.settings.component.html +++ b/src/frontend/app/ui/settings/jobs/jobs.settings.component.html @@ -18,15 +18,13 @@
every - {{periods[schedule.trigger.periodicity]}} {{schedule.trigger.atTime | date:"HH:mm":"+0"}} + {{periods[schedule.trigger.periodicity]}} {{atTimeLocal(schedule.trigger.atTime) | date:"HH:mm (z)"}} {{schedule.trigger.time | date:"medium"}} never - after - : - {{schedule.trigger.afterScheduleName}} + after: {{schedule.trigger.afterScheduleName}} diff --git a/src/frontend/app/ui/settings/jobs/jobs.settings.component.ts b/src/frontend/app/ui/settings/jobs/jobs.settings.component.ts index 1d33e3d77..44acb99e6 100644 --- a/src/frontend/app/ui/settings/jobs/jobs.settings.component.ts +++ b/src/frontend/app/ui/settings/jobs/jobs.settings.component.ts @@ -7,7 +7,8 @@ import {SettingsComponentDirective} from '../_abstract/abstract.settings.compone import {ScheduledJobsService} from '../scheduled-jobs.service'; import { AfterJobTrigger, - JobScheduleDTO, JobScheduleDTOUtils, + JobScheduleDTO, + JobScheduleDTOUtils, JobTriggerType, NeverJobTrigger, PeriodicJobTrigger, @@ -17,7 +18,7 @@ import {ConfigTemplateEntry} from '../../../../../common/entities/job/JobDTO'; import {ModalDirective} from 'ngx-bootstrap/modal'; import {JobProgressDTO, JobProgressStates} from '../../../../../common/entities/job/JobProgressDTO'; import {BackendtextService} from '../../../model/backendtext.service'; -import {ServerConfig, ServerJobConfig} from '../../../../../common/config/private/PrivateConfig'; +import {ServerJobConfig} from '../../../../../common/config/private/PrivateConfig'; @Component({ selector: 'app-settings-jobs', @@ -78,6 +79,14 @@ export class JobsSettingsComponent extends SettingsComponentDirective t.Name === JobName); if (job && job.ConfigTemplate && job.ConfigTemplate.length > 0) { @@ -161,7 +170,7 @@ export class JobsSettingsComponent extends SettingsComponentDirective { + return (this.states.scheduled.value as JobScheduleDTO[]).slice().sort((a: JobScheduleDTO, b: JobScheduleDTO) => { return this.getNextRunningDate(a, this.states.scheduled.value) - this.getNextRunningDate(b, this.states.scheduled.value); }); } @@ -174,7 +183,6 @@ export class JobsSettingsComponent extends SettingsComponentDirective(); date: Date = new Date(); - @Input() name: string; + constructor() { + this.date.setUTCSeconds(0); + this.date.setUTCMilliseconds(0); + } + @Input() public get timestamp(): number { return this.timestampValue; } public set timestamp(val: number) { - this.date.setTime(val + this.timezoneOffset); + const h = Math.min(23, Math.floor(val / 60)); + const m = val % 60; + this.date.setUTCHours(h); + this.date.setUTCMinutes(m); + if (this.timestampValue === val) { return; } this.timestampValue = val; this.timestampChange.emit(this.timestampValue); + } onChange(date: Date | string): void { - this.timestamp = (new Date(date)).getTime() - this.timezoneOffset; + const d = new Date(date); + this.timestamp = d.getUTCHours() * 60 + d.getUTCMinutes(); }