diff --git a/license b/license new file mode 100644 index 0000000..e69066c --- /dev/null +++ b/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Lossless GmbH (hello@lossless.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bd9b19f..d2d1b50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1103,9 +1103,9 @@ } }, "@pushrocks/smartdelay": { - "version": "2.0.6", - "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartdelay/-/smartdelay-2.0.6.tgz", - "integrity": "sha512-4wUnzWNhRPODpaaL5GuRaje/C5dg+TMhBxmr57PKc2fqYpy6azWJwonf/s5xpcbJLCPJRbj1x8M5MqgCFq2uvg==", + "version": "2.0.9", + "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartdelay/-/smartdelay-2.0.9.tgz", + "integrity": "sha512-aVZJit1wq4fBG6ft2LJSPEnNbLSbRUuwNYggSeDqLtY7mXvAQmu4SIsXx18gfOtf69CoZR36RY3ff/IJv62C7w==", "requires": { "@pushrocks/smartpromise": "^3.0.6" } @@ -1494,7 +1494,8 @@ "@types/luxon": { "version": "1.24.0", "resolved": "https://verdaccio.lossless.one/@types%2fluxon/-/luxon-1.24.0.tgz", - "integrity": "sha512-OCTdVRQf/ge2ULlGqVKRI/HwyWoKpLV3ppLt6H27yzK/qdYNJ60Sz5tUxy3Jt1RD9K4Iz1tcDWPFJArBFZNAWQ==" + "integrity": "sha512-OCTdVRQf/ge2ULlGqVKRI/HwyWoKpLV3ppLt6H27yzK/qdYNJ60Sz5tUxy3Jt1RD9K4Iz1tcDWPFJArBFZNAWQ==", + "dev": true }, "@types/mime-types": { "version": "2.1.0", @@ -1932,14 +1933,15 @@ "moment-timezone": "^0.5.x" } }, - "cron-parser": { - "version": "2.14.0", - "resolved": "https://verdaccio.lossless.one/cron-parser/-/cron-parser-2.14.0.tgz", - "integrity": "sha512-/VuS5TLnyaB0yNznygEFmujOjn8DxyZRn3F2wN3h8e+4A5zETQYMbtCLCIvz23XhI/1di2B+ke702/grEaPfTg==", - "requires": { - "is-nan": "^1.3.0", - "moment-timezone": "^0.5.31" - } + "croner": { + "version": "1.1.23", + "resolved": "https://verdaccio.lossless.one/croner/-/croner-1.1.23.tgz", + "integrity": "sha512-VsSyKBVtshU8qd2yPEWsBv5xxTLbRUuq6DX5bgUb4TTn/H6hmFGtfJtr0NKswu1UEsZZ6uhYc0kSOAvPlicQUA==" + }, + "dayjs": { + "version": "1.8.27", + "resolved": "https://verdaccio.lossless.one/dayjs/-/dayjs-1.8.27.tgz", + "integrity": "sha512-Jpa2acjWIeOkg8KURUHICk0EqnEFSSF5eMEscsOgyJ92ZukXwmpmRkPSUka7KHSfbj5eKH30ieosYip+ky9emQ==" }, "debug": { "version": "4.1.1", @@ -2396,7 +2398,8 @@ "luxon": { "version": "1.24.1", "resolved": "https://verdaccio.lossless.one/luxon/-/luxon-1.24.1.tgz", - "integrity": "sha512-CgnIMKAWT0ghcuWFfCWBnWGOddM0zu6c4wZAWmD0NN7MZTnro0+833DF6tJep+xlxRPg4KtsYEHYLfTMBQKwYg==" + "integrity": "sha512-CgnIMKAWT0ghcuWFfCWBnWGOddM0zu6c4wZAWmD0NN7MZTnro0+833DF6tJep+xlxRPg4KtsYEHYLfTMBQKwYg==", + "dev": true }, "magic-string": { "version": "0.25.7", @@ -2473,6 +2476,7 @@ "version": "0.5.31", "resolved": "https://verdaccio.lossless.one/moment-timezone/-/moment-timezone-0.5.31.tgz", "integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==", + "dev": true, "requires": { "moment": ">= 2.9.0" } diff --git a/package.json b/package.json index 9087ec5..b1821ae 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "license": "MIT", "scripts": { "test": "(tstest ./test/)", - "build": "(tsbuild && tsbundle npm --production)" + "build": "(tsbuild && tsbundle npm)" }, "devDependencies": { "@gitzone/tsbuild": "^2.1.11", @@ -22,12 +22,12 @@ "tslint-config-prettier": "^1.18.0" }, "dependencies": { - "@pushrocks/smartdelay": "^2.0.6", + "@pushrocks/smartdelay": "^2.0.9", "@pushrocks/smartpromise": "^3.0.2", "@types/cron": "^1.7.1", - "@types/luxon": "^1.15.1", - "cron-parser": "^2.14.0", - "luxon": "^1.16.0" + "croner": "^1.1.23", + "dayjs": "^1.8.27", + "is-nan": "^1.3.0" }, "files": [ "ts/**/*", diff --git a/test/test.cronmanager.ts b/test/test.cronmanager.ts index 3d8d2c8..87a165c 100644 --- a/test/test.cronmanager.ts +++ b/test/test.cronmanager.ts @@ -12,7 +12,7 @@ tap.test('should create a valid instance of cronmanager', async () => { tap.test('should create a valid cronJon', async tools => { const done = tools.defer(); let counter = 0; - testCronManager.addCronjob('* * * * * *', () => { + testCronManager.addCronjob('*/2 * * * * *', () => { if (counter === 10) { done.resolve(); } diff --git a/ts/index.ts b/ts/index.ts index 9bf5a6a..dceae8e 100644 --- a/ts/index.ts +++ b/ts/index.ts @@ -1,6 +1,7 @@ export * from './smarttime.classes.cronmanager'; export * from './smarttime.classes.extendeddate'; export * from './smarttime.classes.hrtmeasurement'; +export * from './smarttime.classes.interval'; export * from './smarttime.classes.timer'; export * from './smarttime.classes.timestamp'; export * from './smarttime.units'; diff --git a/ts/smarttime.classes.cronjob.ts b/ts/smarttime.classes.cronjob.ts index 71f9c3c..4c0bb5f 100644 --- a/ts/smarttime.classes.cronjob.ts +++ b/ts/smarttime.classes.cronjob.ts @@ -4,48 +4,37 @@ import { CronManager } from './smarttime.classes.cronmanager'; export type TJobFunction = (() => void) | (() => Promise); export class CronJob { + public croner; public status: 'started' | 'stopped' | 'initial' = 'initial'; public cronExpression: string; public jobFunction: TJobFunction; - - private cronInterval = plugins.cronParser.parseExpression('* * * * * *'); private nextExecutionUnix: number = 0; constructor(cronManager: CronManager, cronExpressionArg: string, jobFunction: TJobFunction) { this.cronExpression = cronExpressionArg; this.jobFunction = jobFunction; + this.croner = plugins.croner(this.cronExpression); } /** * checks wether the cronjob needs to be executed */ - public checkExecution() { + public checkExecution(): number { if (this.nextExecutionUnix === 0) { - this.nextExecutionUnix = this.cronInterval - .next() - .toDate() - .getTime(); - return; + this.nextExecutionUnix = this.croner.msToNext(); } if (Date.now() > this.nextExecutionUnix) { this.jobFunction(); - this.nextExecutionUnix = this.cronInterval - .next() - .toDate() - .getTime(); + this.nextExecutionUnix = this.croner.msToNext(); } + return this.nextExecutionUnix; } public start() { - this.cronInterval = this.getCronInterval(); this.status = 'started'; } public stop() { this.status = 'stopped'; } - - private getCronInterval() { - return plugins.cronParser.parseExpression(this.cronExpression); - } } diff --git a/ts/smarttime.classes.cronmanager.ts b/ts/smarttime.classes.cronmanager.ts index d1a2e58..f790225 100644 --- a/ts/smarttime.classes.cronmanager.ts +++ b/ts/smarttime.classes.cronmanager.ts @@ -1,21 +1,13 @@ import * as plugins from './smarttime.plugins'; import { CronJob } from './smarttime.classes.cronjob'; -import { Timer } from './smarttime.classes.timer'; -import { Interval } from './smarttime.classes.interval'; export class CronManager { - public cronInterval = new Interval(1000); + public executionTimeout: plugins.smartdelay.Timeout; public status: 'started' | 'stopped' = 'stopped'; public cronjobs: CronJob[] = []; - constructor() { - this.cronInterval.addIntervalJob(() => { - for (const cronJob of this.cronjobs) { - cronJob.checkExecution(); - } - }); - } + constructor() {} public addCronjob(cronIdentifierArg: string, cronFunctionArg: () => any) { const newCronJob = new CronJob(this, cronIdentifierArg, cronFunctionArg); @@ -30,10 +22,28 @@ export class CronManager { */ public start() { this.status = 'started'; - for (const cron of this.cronjobs) { - cron.start(); + for (const cronJob of this.cronjobs) { + cronJob.start(); } - this.cronInterval.start(); + this.executionTimeout = new plugins.smartdelay.Timeout(0); + + // recursion + const runCheckExecution = () => { + console.log(`Next CronJob scheduled in ${this.executionTimeout.getTimeLeft()} milliseconds`); + this.executionTimeout.promise.then(() => { + let timeToNextOverallExecution: number; + for (const cronJob of this.cronjobs) { + const timeToNextJobExecution = cronJob.checkExecution(); + if (timeToNextJobExecution < timeToNextOverallExecution || !timeToNextOverallExecution) { + timeToNextOverallExecution = timeToNextJobExecution; + } + } + this.executionTimeout = new plugins.smartdelay.Timeout(timeToNextOverallExecution); + runCheckExecution(); + }); + }; + + runCheckExecution(); } /** @@ -41,9 +51,9 @@ export class CronManager { */ public stop() { this.status = 'stopped'; + this.executionTimeout.cancel(); for (const cron of this.cronjobs) { cron.stop(); } - this.cronInterval.stop(); } } diff --git a/ts/smarttime.classes.extendeddate.ts b/ts/smarttime.classes.extendeddate.ts index 862cf03..936e015 100644 --- a/ts/smarttime.classes.extendeddate.ts +++ b/ts/smarttime.classes.extendeddate.ts @@ -26,12 +26,12 @@ export class ExtendedDate extends Date { public static fromEuropeanDate(europeanDate: string) { const dateArray = /(.*)\.(.*)\.(.*)/.exec(europeanDate); - const luxonDate = plugins.luxon.DateTime.utc( + const date = new Date( parseFloat(dateArray[3]), // year - parseFloat(dateArray[2]), // month + parseFloat(dateArray[2]) - 1, // month parseFloat(dateArray[1]) // day ); - const unixMilli = luxonDate.toMillis(); + const unixMilli = date.getTime(); return new ExtendedDate(unixMilli); } @@ -64,10 +64,8 @@ export class ExtendedDate extends Date { const dateTimeString = `${dateArray[3]}-${sliceDate(dateArray[2])}-${sliceDate( dateArray[1] )}T${timeArg}`; - const luxonDate = plugins.luxon.DateTime.fromISO(dateTimeString, { - zone: zoneArg - }); - const unixMilli = luxonDate.toMillis(); + const date = plugins.dayjs(dateTimeString); + const unixMilli = date.toDate().getTime(); return new ExtendedDate(unixMilli); } diff --git a/ts/smarttime.classes.interval.ts b/ts/smarttime.classes.interval.ts index d427c8a..bc7cbc5 100644 --- a/ts/smarttime.classes.interval.ts +++ b/ts/smarttime.classes.interval.ts @@ -4,7 +4,10 @@ export class Interval { public status: 'started' | 'stopped' | 'initial' = 'initial'; private statusAuthorization: any = null; + // timings public intervalMilliseconds: number; + public nextIntervalMillisenconds: number; + public intervalJobs: Array<() => any> = []; constructor(intervalMillisencondsArg: number) { this.intervalMilliseconds = intervalMillisencondsArg; diff --git a/ts/smarttime.plugins.ts b/ts/smarttime.plugins.ts index 7078769..3fa8a8a 100644 --- a/ts/smarttime.plugins.ts +++ b/ts/smarttime.plugins.ts @@ -5,7 +5,7 @@ import * as smartpromise from '@pushrocks/smartpromise'; export { smartdelay, smartpromise }; // third parties -import cronParser from 'cron-parser'; -import * as luxon from 'luxon'; +import croner from 'croner'; +import dayjs from 'dayjs'; -export { cronParser, luxon }; +export { croner, dayjs };