cron-converter-u2q 1.2.1 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -80
- package/lib/converter.d.ts +17 -4
- package/lib/converter.js +93 -28
- package/lib/describer.d.ts +41 -0
- package/lib/describer.js +272 -0
- package/lib/helper.d.ts +7 -0
- package/lib/helper.js +22 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/lib/validator.d.ts +6 -0
- package/lib/validator.js +215 -0
- package/package.json +4 -1
- package/src/__tests__/index.test.ts +194 -34
- package/src/__tests__/specs-compliance.test.ts +137 -0
- package/src/converter.ts +43 -33
- package/src/describer.ts +169 -57
- package/src/helper.ts +2 -2
- package/src/index.ts +1 -0
- package/src/validator.ts +228 -0
- package/types/index.d.ts +15 -5
package/README.md
CHANGED
|
@@ -1,123 +1,93 @@
|
|
|
1
1
|
# cron-converter-u2q
|
|
2
2
|
|
|
3
|
-
[](https://github.com/rahu619/cron-converter-u2q)
|
|
4
3
|
[](https://www.npmjs.com/package/cron-converter-u2q)
|
|
4
|
+
[](https://www.npmjs.com/package/cron-converter-u2q)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://github.com/rahu619/cron-converter-u2q/actions)
|
|
7
|
-
[](https://github.com/rahu619/cron-converter-u2q/actions)
|
|
8
|
-
[](https://www.typescriptlang.org/)
|
|
9
|
-
[](https://www.npmjs.com/package/cron-converter-u2q)
|
|
10
|
-
|
|
11
|
-
[](https://www.npmjs.com/package/cron-converter-u2q)
|
|
12
7
|
|
|
13
|
-
A
|
|
8
|
+
A lightweight, zero-dependency TypeScript utility to convert, validate, and describe cron expressions. Seamlessly translate between Unix and Quartz schedules, perform boundary checks, and render human-readable descriptions.
|
|
14
9
|
|
|
15
|
-
|
|
10
|
+
---
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
- **Unix to Quartz**: Convert standard Unix cron expressions to Quartz format
|
|
19
|
-
- **Quartz to Unix**: Convert Quartz cron expressions to standard Unix format
|
|
20
|
-
- **Format Validation**: Built-in validation for both formats
|
|
21
|
-
- **Error Handling**: Clear error messages for invalid expressions
|
|
12
|
+
## 💡 Unix vs. Quartz Cron
|
|
22
13
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- **Detailed**: Includes all schedule details (minutes, hours, days, etc.)
|
|
14
|
+
| Specification | Fields | Format | Support |
|
|
15
|
+
| :--- | :--- | :--- | :--- |
|
|
16
|
+
| **Unix Cron** | 5 fields | `min hour dom month dow` | Standard POSIX (Linux, Crontab) |
|
|
17
|
+
| **Quartz Cron** | 6–7 fields | `sec min hour dom month dow [year]` | Java/Spring, AWS EventBridge, Azure |
|
|
28
18
|
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
- **Zero Dependencies**: Lightweight and fast
|
|
32
|
-
- **Well Tested**: Comprehensive test coverage
|
|
33
|
-
- **ES6 Modules**: Support for both CommonJS and ES6 imports
|
|
19
|
+
> [!NOTE]
|
|
20
|
+
> Quartz cron requires exactly one of `day-of-month` or `day-of-week` to contain `?` (when specifying a constraint on the other field) to prevent scheduling conflicts.
|
|
34
21
|
|
|
35
|
-
|
|
36
|
-
- **Format Validation**: Ensures cron expressions are valid
|
|
37
|
-
- **Range Checking**: Validates field values within acceptable ranges
|
|
38
|
-
- **Clear Errors**: Descriptive error messages for debugging
|
|
39
|
-
- **Type Safety**: TypeScript types for better development experience
|
|
22
|
+
---
|
|
40
23
|
|
|
41
24
|
## 📦 Installation
|
|
42
25
|
|
|
43
26
|
```bash
|
|
44
|
-
# Using npm
|
|
45
27
|
npm install cron-converter-u2q
|
|
46
|
-
|
|
47
|
-
# Using yarn
|
|
28
|
+
# or
|
|
48
29
|
yarn add cron-converter-u2q
|
|
49
|
-
|
|
50
|
-
# Using pnpm
|
|
30
|
+
# or
|
|
51
31
|
pnpm add cron-converter-u2q
|
|
52
32
|
```
|
|
53
33
|
|
|
54
|
-
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 🚀 Usage
|
|
37
|
+
|
|
38
|
+
### 1. Two-Way Conversion
|
|
39
|
+
Convert seamlessly between formats. Wildcards and aliases are normalized automatically.
|
|
55
40
|
|
|
56
41
|
```typescript
|
|
57
42
|
import { CronConverterU2Q } from 'cron-converter-u2q';
|
|
58
43
|
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// Convert Quartz to Unix
|
|
64
|
-
const unixExpression = CronConverterU2Q.quartzToUnix('0 0 8 * * ?');
|
|
65
|
-
console.log(unixExpression); // "0 8 * * *"
|
|
44
|
+
// Unix -> Quartz (adds 0 seconds)
|
|
45
|
+
CronConverterU2Q.unixToQuartz('*/15 * * * *'); // "0 */15 * * * * *"
|
|
46
|
+
CronConverterU2Q.unixToQuartz('0 12 * * 1'); // "0 0 12 ? * 2 *"
|
|
66
47
|
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
48
|
+
// Quartz -> Unix (drops seconds/year)
|
|
49
|
+
CronConverterU2Q.quartzToUnix('0 0 8 * * ?'); // "0 8 * * *"
|
|
50
|
+
CronConverterU2Q.quartzToUnix('0 */5 * ? * 2'); // "*/5 * * * 1"
|
|
70
51
|
```
|
|
71
52
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
### Basic Conversions
|
|
53
|
+
### 2. Validation
|
|
54
|
+
Perform boundary checks, detect syntax errors, and validate range limits.
|
|
75
55
|
|
|
76
56
|
```typescript
|
|
77
|
-
|
|
78
|
-
CronConverterU2Q.unixToQuartz('0 12 * * *'); // "0 0 12 * * ? *"
|
|
79
|
-
CronConverterU2Q.unixToQuartz('*/15 * * * *'); // "0 */15 * * * ? *"
|
|
57
|
+
import { CronValidatorU2Q } from 'cron-converter-u2q';
|
|
80
58
|
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
59
|
+
// Validate and throw detailed error messages
|
|
60
|
+
CronValidatorU2Q.validateUnix('60 * * * *'); // Throws: "Value 60 is out of range (0-59) for Minute"
|
|
61
|
+
CronValidatorU2Q.validateQuartz('0 */0 * ? * *'); // Throws: "Invalid step value in Minute: 0"
|
|
62
|
+
|
|
63
|
+
// Boolean check
|
|
64
|
+
CronValidatorU2Q.isValidUnix('*/5 * * * *'); // true
|
|
65
|
+
CronValidatorU2Q.isValidQuartz('0 0 12 * * * *'); // true (both are allowed to be '*' in this package)
|
|
84
66
|
```
|
|
85
67
|
|
|
86
|
-
### Human-
|
|
68
|
+
### 3. Human-Readable Descriptions
|
|
69
|
+
Render cron schedules into clear, natural English. Fully supports lists, ranges, step modifiers, and Quartz special symbols.
|
|
87
70
|
|
|
88
71
|
```typescript
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
72
|
+
import { CronDescriberU2Q } from 'cron-converter-u2q';
|
|
73
|
+
|
|
74
|
+
// Unix
|
|
75
|
+
CronDescriberU2Q.describeUnix('*/15 * * * *'); // "Every 15 minutes"
|
|
76
|
+
CronDescriberU2Q.describeUnix('0 12 1-5 * *'); // "At 12 o'clock from the 1st to the 5th of the month"
|
|
92
77
|
|
|
93
|
-
// Quartz
|
|
94
|
-
|
|
95
|
-
|
|
78
|
+
// Quartz
|
|
79
|
+
CronDescriberU2Q.describeQuartz('0 0 8,12 ? * 2-6 *'); // "At 8 and 12 o'clock from Monday to Friday"
|
|
80
|
+
CronDescriberU2Q.describeQuartz('0 0 0 L * ?'); // "At 0 o'clock on the last day of the month"
|
|
96
81
|
```
|
|
97
82
|
|
|
83
|
+
---
|
|
84
|
+
|
|
98
85
|
## 🤝 Contributing
|
|
99
86
|
|
|
100
|
-
Contributions are welcome! Please feel free to
|
|
87
|
+
Contributions are welcome! Please feel free to open issues or submit Pull Requests.
|
|
101
88
|
|
|
102
|
-
|
|
103
|
-
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
104
|
-
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
105
|
-
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
106
|
-
5. Open a Pull Request
|
|
89
|
+
---
|
|
107
90
|
|
|
108
91
|
## 📄 License
|
|
109
92
|
|
|
110
|
-
This project is licensed under the MIT License
|
|
111
|
-
|
|
112
|
-
## 💬 Support
|
|
113
|
-
|
|
114
|
-
- 📧 Email: rahu619@gmail.com
|
|
115
|
-
- 💻 GitHub Issues: [Create an issue](https://github.com/rahu619/cron-converter-u2q/issues)
|
|
116
|
-
- ⭐ Star the repository if you find it useful!
|
|
117
|
-
|
|
118
|
-
## 🙏 Acknowledgments
|
|
119
|
-
|
|
120
|
-
- Thanks to all contributors who have helped shape this project
|
|
121
|
-
- Inspired by the need for a simple, reliable cron expression converter
|
|
122
|
-
- Built with TypeScript for better developer experience
|
|
123
|
-
|
|
93
|
+
This project is licensed under the MIT License.
|
package/lib/converter.d.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
export declare class CronConverterU2Q {
|
|
2
|
-
static readonly delimiter = " ";
|
|
3
|
-
static readonly unixExpressionLength = 5;
|
|
4
|
-
static readonly quartzExpressionLengths: number[];
|
|
5
2
|
/**
|
|
6
3
|
* Converts a unix cron expression to a quartz cron expression by adding '0' seconds
|
|
7
4
|
* @param unixExpression - the unix expression
|
|
@@ -14,5 +11,21 @@ export declare class CronConverterU2Q {
|
|
|
14
11
|
* @returns the corresponding unix expression
|
|
15
12
|
*/
|
|
16
13
|
static quartzToUnix(quartzExpression: string): string;
|
|
17
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Converts interval parts for both Unix and Quartz expressions.
|
|
16
|
+
* In both directions, normalises step notation to the Unix `*\/N` format.
|
|
17
|
+
*/
|
|
18
|
+
private static convertIntervalParts;
|
|
19
|
+
/**
|
|
20
|
+
* Converts Unix DOW to Quartz DOW, supporting lists, ranges, and special cases.
|
|
21
|
+
* Unix: 0=Sun, 1=Mon, ..., 6=Sat, 7=Sun(alias)
|
|
22
|
+
* Quartz: 1=Sun, 2=Mon, ..., 7=Sat
|
|
23
|
+
*/
|
|
24
|
+
private static unixDowToQuartz;
|
|
25
|
+
/**
|
|
26
|
+
* Converts Quartz DOW to Unix DOW, supporting lists, ranges, and special cases.
|
|
27
|
+
* Quartz: 1=Sun, 2=Mon, ..., 7=Sat
|
|
28
|
+
* Unix: 0=Sun, 1=Mon, ..., 6=Sat
|
|
29
|
+
*/
|
|
30
|
+
static quartzDowToUnix(dow: string): string;
|
|
18
31
|
}
|
package/lib/converter.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CronConverterU2Q = void 0;
|
|
4
|
+
const helper_1 = require("./helper");
|
|
5
|
+
const validator_1 = require("./validator");
|
|
4
6
|
class CronConverterU2Q {
|
|
5
7
|
/**
|
|
6
8
|
* Converts a unix cron expression to a quartz cron expression by adding '0' seconds
|
|
@@ -8,18 +10,21 @@ class CronConverterU2Q {
|
|
|
8
10
|
* @returns the corresponding quartz expression
|
|
9
11
|
*/
|
|
10
12
|
static unixToQuartz(unixExpression) {
|
|
11
|
-
|
|
12
|
-
const parts =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
validator_1.CronValidatorU2Q.validateUnix(unixExpression);
|
|
14
|
+
const parts = helper_1.ExpressionHelper.GetExpressionParts(unixExpression);
|
|
15
|
+
const [min, hour, dom, month, dow] = parts.map(part => this.convertIntervalParts(part));
|
|
16
|
+
// Enhanced DOW conversion: handle lists, ranges, and special cases
|
|
17
|
+
let quartzDow = this.unixDowToQuartz(dow);
|
|
16
18
|
let quartzDom = dom;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
// Per Quartz spec, exactly one of DOM or DOW must be '?'
|
|
20
|
+
// When DOM is '*' and DOW is specific, DOM gets '?'; otherwise DOW gets '?'
|
|
21
|
+
if (dom === '*' && dow !== '*') {
|
|
19
22
|
quartzDom = '?';
|
|
20
|
-
|
|
23
|
+
}
|
|
24
|
+
else if (dom !== '*' && dow === '*') {
|
|
21
25
|
quartzDow = '?';
|
|
22
|
-
|
|
26
|
+
}
|
|
27
|
+
return `0 ${min} ${hour} ${quartzDom} ${month} ${quartzDow} *`;
|
|
23
28
|
}
|
|
24
29
|
/**
|
|
25
30
|
* Converts a quartz cron expression to a unix cron expression
|
|
@@ -27,27 +32,87 @@ class CronConverterU2Q {
|
|
|
27
32
|
* @returns the corresponding unix expression
|
|
28
33
|
*/
|
|
29
34
|
static quartzToUnix(quartzExpression) {
|
|
30
|
-
|
|
31
|
-
const parts =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
unixDom = '*';
|
|
35
|
+
validator_1.CronValidatorU2Q.validateQuartz(quartzExpression);
|
|
36
|
+
const parts = helper_1.ExpressionHelper.GetExpressionParts(quartzExpression);
|
|
37
|
+
const [_, min, hour, dom, month, dow] = parts.map(part => this.convertIntervalParts(part, true));
|
|
38
|
+
if (dom.includes('L') || dom.includes('W')) {
|
|
39
|
+
throw new Error("Unix cron does not support 'L' or 'W' in Day of Month");
|
|
40
|
+
}
|
|
41
|
+
if (dow.includes('L') || dow.includes('#')) {
|
|
42
|
+
throw new Error("Unix cron does not support 'L' or '#' in Day of Week");
|
|
43
|
+
}
|
|
44
|
+
// Enhanced DOW conversion: handle lists, ranges, and special cases
|
|
45
|
+
let unixDow = this.quartzDowToUnix(dow);
|
|
46
|
+
let unixDom = dom === '?' ? '*' : dom;
|
|
43
47
|
return `${min} ${hour} ${unixDom} ${month} ${unixDow}`;
|
|
44
48
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Converts interval parts for both Unix and Quartz expressions.
|
|
51
|
+
* In both directions, normalises step notation to the Unix `*\/N` format.
|
|
52
|
+
*/
|
|
53
|
+
static convertIntervalParts(part, isQuartz = false) {
|
|
54
|
+
if (isQuartz) {
|
|
55
|
+
return part.replace(/^0\/(\d+)$/, '*/$1');
|
|
56
|
+
}
|
|
57
|
+
return part;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Converts Unix DOW to Quartz DOW, supporting lists, ranges, and special cases.
|
|
61
|
+
* Unix: 0=Sun, 1=Mon, ..., 6=Sat, 7=Sun(alias)
|
|
62
|
+
* Quartz: 1=Sun, 2=Mon, ..., 7=Sat
|
|
63
|
+
*/
|
|
64
|
+
static unixDowToQuartz(dow) {
|
|
65
|
+
if (dow === '*' || dow === '?')
|
|
66
|
+
return dow;
|
|
67
|
+
if (dow.includes(','))
|
|
68
|
+
return dow.split(',').map(d => this.unixDowToQuartz(d)).join(',');
|
|
69
|
+
if (dow.includes('-'))
|
|
70
|
+
return dow.split('-').map(d => this.unixDowToQuartz(d)).join('-');
|
|
71
|
+
if (dow.endsWith('L')) {
|
|
72
|
+
const day = dow.slice(0, -1);
|
|
73
|
+
return `${this.unixDowToQuartz(day)}L`;
|
|
74
|
+
}
|
|
75
|
+
if (dow.includes('#')) {
|
|
76
|
+
const [day, nth] = dow.split('#');
|
|
77
|
+
return `${this.unixDowToQuartz(day)}#${nth}`;
|
|
78
|
+
}
|
|
79
|
+
if (dow === '0' || dow === '7')
|
|
80
|
+
return '1'; // Sunday
|
|
81
|
+
const num = parseInt(dow, 10);
|
|
82
|
+
if (!isNaN(num) && num >= 1 && num <= 6)
|
|
83
|
+
return (num + 1).toString(); // Mon(1)→2 … Sat(6)→7
|
|
84
|
+
return dow;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Converts Quartz DOW to Unix DOW, supporting lists, ranges, and special cases.
|
|
88
|
+
* Quartz: 1=Sun, 2=Mon, ..., 7=Sat
|
|
89
|
+
* Unix: 0=Sun, 1=Mon, ..., 6=Sat
|
|
90
|
+
*/
|
|
91
|
+
static quartzDowToUnix(dow) {
|
|
92
|
+
if (dow === '*' || dow === '?')
|
|
93
|
+
return dow === '?' ? '*' : dow;
|
|
94
|
+
// Split compound expressions so each element is converted individually
|
|
95
|
+
if (dow.includes(','))
|
|
96
|
+
return dow.split(',').map(d => this.quartzDowToUnix(d)).join(',');
|
|
97
|
+
if (dow.includes('-'))
|
|
98
|
+
return dow.split('-').map(d => this.quartzDowToUnix(d)).join('-');
|
|
99
|
+
// Last (L) — convert the numeric day part, preserve L suffix
|
|
100
|
+
if (dow.endsWith('L')) {
|
|
101
|
+
const day = dow.slice(0, -1);
|
|
102
|
+
return `${this.quartzDowToUnix(day)}L`;
|
|
103
|
+
}
|
|
104
|
+
// Nth weekday (#) — convert the numeric day part, preserve #N
|
|
105
|
+
if (dow.includes('#')) {
|
|
106
|
+
const [day, nth] = dow.split('#');
|
|
107
|
+
return `${this.quartzDowToUnix(day)}#${nth}`;
|
|
108
|
+
}
|
|
109
|
+
// Numeric mapping
|
|
110
|
+
if (dow === '1')
|
|
111
|
+
return '0'; // Sunday
|
|
112
|
+
const num = parseInt(dow, 10);
|
|
113
|
+
if (!isNaN(num) && num >= 2 && num <= 7)
|
|
114
|
+
return (num - 1).toString(); // Mon(2)→1 … Sat(7)→6
|
|
115
|
+
return dow;
|
|
48
116
|
}
|
|
49
117
|
}
|
|
50
118
|
exports.CronConverterU2Q = CronConverterU2Q;
|
|
51
|
-
CronConverterU2Q.delimiter = ' ';
|
|
52
|
-
CronConverterU2Q.unixExpressionLength = 5;
|
|
53
|
-
CronConverterU2Q.quartzExpressionLengths = [6, 7];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare class CronDescriberU2Q {
|
|
2
|
+
/**
|
|
3
|
+
* Generates a human-readable description for a Unix-style cron expression.
|
|
4
|
+
*
|
|
5
|
+
* Unix-style cron expressions consist of 5 parts:
|
|
6
|
+
* - Minute (0-59)
|
|
7
|
+
* - Hour (0-23)
|
|
8
|
+
* - Day of Month (1-31)
|
|
9
|
+
* - Month (1-12)
|
|
10
|
+
* - Day of Week (0-6, where 0 = Sunday)
|
|
11
|
+
* @param unixExpression - A string containing the Unix-style cron expression.
|
|
12
|
+
* @returns A human-readable description or an error message if invalid.
|
|
13
|
+
*/
|
|
14
|
+
static describeUnix(unixExpression: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Generates a human-readable description for a Quartz-style cron expression.
|
|
17
|
+
*
|
|
18
|
+
* Quartz-style cron expressions consist of 6 or 7 parts:
|
|
19
|
+
* - Second (0-59)
|
|
20
|
+
* - Minute (0-59)
|
|
21
|
+
* - Hour (0-23)
|
|
22
|
+
* - Day of Month (1-31)
|
|
23
|
+
* - Month (1-12)
|
|
24
|
+
* - Day of Week (1-7, where 1 = Sunday)
|
|
25
|
+
* - Year (optional)
|
|
26
|
+
* @param quartzExpression - A string containing the Quartz-style cron expression.
|
|
27
|
+
* @returns A human-readable description or an error message if invalid.
|
|
28
|
+
*/
|
|
29
|
+
static describeQuartz(quartzExpression: string): string;
|
|
30
|
+
private static describeSecond;
|
|
31
|
+
private static describeMinute;
|
|
32
|
+
private static describeHour;
|
|
33
|
+
private static describeDayOfMonth;
|
|
34
|
+
private static describeMonth;
|
|
35
|
+
private static describeDayOfWeek;
|
|
36
|
+
private static describeYear;
|
|
37
|
+
private static resolveValue;
|
|
38
|
+
private static describeField;
|
|
39
|
+
private static ordinalSuffix;
|
|
40
|
+
private static combineDescriptions;
|
|
41
|
+
}
|
package/lib/describer.js
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CronDescriberU2Q = void 0;
|
|
4
|
+
const helper_1 = require("./helper");
|
|
5
|
+
const validator_1 = require("./validator");
|
|
6
|
+
const converter_1 = require("./converter");
|
|
7
|
+
class CronDescriberU2Q {
|
|
8
|
+
/**
|
|
9
|
+
* Generates a human-readable description for a Unix-style cron expression.
|
|
10
|
+
*
|
|
11
|
+
* Unix-style cron expressions consist of 5 parts:
|
|
12
|
+
* - Minute (0-59)
|
|
13
|
+
* - Hour (0-23)
|
|
14
|
+
* - Day of Month (1-31)
|
|
15
|
+
* - Month (1-12)
|
|
16
|
+
* - Day of Week (0-6, where 0 = Sunday)
|
|
17
|
+
* @param unixExpression - A string containing the Unix-style cron expression.
|
|
18
|
+
* @returns A human-readable description or an error message if invalid.
|
|
19
|
+
*/
|
|
20
|
+
static describeUnix(unixExpression) {
|
|
21
|
+
try {
|
|
22
|
+
validator_1.CronValidatorU2Q.validateUnix(unixExpression);
|
|
23
|
+
const parts = helper_1.ExpressionHelper.GetExpressionParts(unixExpression);
|
|
24
|
+
const [min, hour, dom, month, dow] = parts;
|
|
25
|
+
const descriptions = [
|
|
26
|
+
this.describeMinute(min, true),
|
|
27
|
+
this.describeHour(hour),
|
|
28
|
+
this.describeDayOfMonth(dom),
|
|
29
|
+
this.describeMonth(month),
|
|
30
|
+
this.describeDayOfWeek(dow),
|
|
31
|
+
];
|
|
32
|
+
return this.combineDescriptions(descriptions);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
return "Invalid Unix cron expression";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Generates a human-readable description for a Quartz-style cron expression.
|
|
40
|
+
*
|
|
41
|
+
* Quartz-style cron expressions consist of 6 or 7 parts:
|
|
42
|
+
* - Second (0-59)
|
|
43
|
+
* - Minute (0-59)
|
|
44
|
+
* - Hour (0-23)
|
|
45
|
+
* - Day of Month (1-31)
|
|
46
|
+
* - Month (1-12)
|
|
47
|
+
* - Day of Week (1-7, where 1 = Sunday)
|
|
48
|
+
* - Year (optional)
|
|
49
|
+
* @param quartzExpression - A string containing the Quartz-style cron expression.
|
|
50
|
+
* @returns A human-readable description or an error message if invalid.
|
|
51
|
+
*/
|
|
52
|
+
static describeQuartz(quartzExpression) {
|
|
53
|
+
try {
|
|
54
|
+
validator_1.CronValidatorU2Q.validateQuartz(quartzExpression);
|
|
55
|
+
const parts = helper_1.ExpressionHelper.GetExpressionParts(quartzExpression);
|
|
56
|
+
const [second, min, hour, dom, month, dow, year] = parts;
|
|
57
|
+
// Normalize Quartz DOW to Unix DOW for consistent description mapping
|
|
58
|
+
const normalizedDow = converter_1.CronConverterU2Q.quartzDowToUnix(dow);
|
|
59
|
+
const descriptions = [
|
|
60
|
+
this.describeSecond(second, true),
|
|
61
|
+
this.describeMinute(min, true),
|
|
62
|
+
this.describeHour(hour),
|
|
63
|
+
this.describeDayOfMonth(dom),
|
|
64
|
+
this.describeMonth(month),
|
|
65
|
+
this.describeDayOfWeek(normalizedDow),
|
|
66
|
+
this.describeYear(year),
|
|
67
|
+
];
|
|
68
|
+
return this.combineDescriptions(descriptions);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
return "Invalid Quartz cron expression";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
static describeSecond(second, suppressZero = false) {
|
|
75
|
+
if (second === "*" || (suppressZero && second === "0"))
|
|
76
|
+
return "";
|
|
77
|
+
const desc = this.describeField(second, { nameMap: null, unitSingular: "second", unitPlural: "seconds" });
|
|
78
|
+
if (desc.startsWith("every") || desc.startsWith("from")) {
|
|
79
|
+
return desc.charAt(0).toUpperCase() + desc.slice(1);
|
|
80
|
+
}
|
|
81
|
+
return `At second ${desc}`;
|
|
82
|
+
}
|
|
83
|
+
static describeMinute(min, suppressZero = false) {
|
|
84
|
+
if (min === "*" || (suppressZero && min === "0"))
|
|
85
|
+
return "";
|
|
86
|
+
const desc = this.describeField(min, { nameMap: null, unitSingular: "minute", unitPlural: "minutes" });
|
|
87
|
+
if (desc.startsWith("every") || desc.startsWith("from")) {
|
|
88
|
+
return desc.charAt(0).toUpperCase() + desc.slice(1);
|
|
89
|
+
}
|
|
90
|
+
return `At minute ${desc}`;
|
|
91
|
+
}
|
|
92
|
+
static describeHour(hour) {
|
|
93
|
+
if (hour === "*")
|
|
94
|
+
return "";
|
|
95
|
+
const desc = this.describeField(hour, { nameMap: null, unitSingular: "hour", unitPlural: "hours" });
|
|
96
|
+
if (desc.startsWith("every") || desc.startsWith("from")) {
|
|
97
|
+
return desc.charAt(0).toUpperCase() + desc.slice(1);
|
|
98
|
+
}
|
|
99
|
+
return `At ${desc} o'clock`;
|
|
100
|
+
}
|
|
101
|
+
static describeDayOfMonth(dom) {
|
|
102
|
+
const desc = this.describeField(dom, {
|
|
103
|
+
nameMap: null,
|
|
104
|
+
unitSingular: "day of month",
|
|
105
|
+
unitPlural: "days of month",
|
|
106
|
+
isDom: true
|
|
107
|
+
});
|
|
108
|
+
if (!desc)
|
|
109
|
+
return "";
|
|
110
|
+
if (desc.startsWith("every") || desc.startsWith("from"))
|
|
111
|
+
return desc;
|
|
112
|
+
return `on the ${desc} of the month`;
|
|
113
|
+
}
|
|
114
|
+
static describeMonth(month) {
|
|
115
|
+
const desc = this.describeField(month, {
|
|
116
|
+
nameMap: [
|
|
117
|
+
"January",
|
|
118
|
+
"February",
|
|
119
|
+
"March",
|
|
120
|
+
"April",
|
|
121
|
+
"May",
|
|
122
|
+
"June",
|
|
123
|
+
"July",
|
|
124
|
+
"August",
|
|
125
|
+
"September",
|
|
126
|
+
"October",
|
|
127
|
+
"November",
|
|
128
|
+
"December",
|
|
129
|
+
],
|
|
130
|
+
unitSingular: "month",
|
|
131
|
+
unitPlural: "months"
|
|
132
|
+
});
|
|
133
|
+
if (!desc)
|
|
134
|
+
return "";
|
|
135
|
+
if (desc.startsWith("every") || desc.startsWith("from"))
|
|
136
|
+
return desc;
|
|
137
|
+
return `in ${desc}`;
|
|
138
|
+
}
|
|
139
|
+
static describeDayOfWeek(dow) {
|
|
140
|
+
const desc = this.describeField(dow, {
|
|
141
|
+
nameMap: [
|
|
142
|
+
"Sunday",
|
|
143
|
+
"Monday",
|
|
144
|
+
"Tuesday",
|
|
145
|
+
"Wednesday",
|
|
146
|
+
"Thursday",
|
|
147
|
+
"Friday",
|
|
148
|
+
"Saturday",
|
|
149
|
+
],
|
|
150
|
+
unitSingular: "day of week",
|
|
151
|
+
unitPlural: "days of week",
|
|
152
|
+
isDow: true
|
|
153
|
+
});
|
|
154
|
+
if (!desc)
|
|
155
|
+
return "";
|
|
156
|
+
if (desc.startsWith("every") || desc.startsWith("from"))
|
|
157
|
+
return desc;
|
|
158
|
+
return `on ${desc}`;
|
|
159
|
+
}
|
|
160
|
+
static describeYear(year) {
|
|
161
|
+
if (!year || year === "*")
|
|
162
|
+
return "";
|
|
163
|
+
const desc = this.describeField(year, {
|
|
164
|
+
nameMap: null,
|
|
165
|
+
unitSingular: "year",
|
|
166
|
+
unitPlural: "years"
|
|
167
|
+
});
|
|
168
|
+
if (!desc)
|
|
169
|
+
return "";
|
|
170
|
+
if (desc.startsWith("every") || desc.startsWith("from"))
|
|
171
|
+
return desc;
|
|
172
|
+
return `in ${desc}`;
|
|
173
|
+
}
|
|
174
|
+
static resolveValue(val, config) {
|
|
175
|
+
if (config.isDow) {
|
|
176
|
+
if (val.endsWith("L")) {
|
|
177
|
+
const day = val.slice(0, -1);
|
|
178
|
+
return `last ${this.resolveValue(day, config)}`;
|
|
179
|
+
}
|
|
180
|
+
if (val.includes("#")) {
|
|
181
|
+
const [day, nth] = val.split("#");
|
|
182
|
+
const nthStr = this.ordinalSuffix(Number(nth));
|
|
183
|
+
return `${nthStr} ${this.resolveValue(day, config)}`;
|
|
184
|
+
}
|
|
185
|
+
let dowVal = val.toUpperCase();
|
|
186
|
+
const dayNames = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
|
|
187
|
+
const aliasIndex = dayNames.indexOf(dowVal);
|
|
188
|
+
let index = aliasIndex !== -1 ? aliasIndex : Number(val);
|
|
189
|
+
if (index === 7)
|
|
190
|
+
index = 0;
|
|
191
|
+
return config.nameMap ? config.nameMap[index] || val : val;
|
|
192
|
+
}
|
|
193
|
+
if (config.isDom) {
|
|
194
|
+
if (val === "L")
|
|
195
|
+
return "last day";
|
|
196
|
+
if (val === "LW")
|
|
197
|
+
return "last weekday";
|
|
198
|
+
if (val.endsWith("W")) {
|
|
199
|
+
const day = val.slice(0, -1);
|
|
200
|
+
return `nearest weekday to the ${this.resolveValue(day, config)}`;
|
|
201
|
+
}
|
|
202
|
+
if (val.startsWith("L-")) {
|
|
203
|
+
const offset = val.slice(2);
|
|
204
|
+
return `${offset} days before the last day`;
|
|
205
|
+
}
|
|
206
|
+
return this.ordinalSuffix(Number(val));
|
|
207
|
+
}
|
|
208
|
+
if (config.nameMap) {
|
|
209
|
+
let mVal = val.toUpperCase();
|
|
210
|
+
const monthNames = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
|
|
211
|
+
const aliasIndex = monthNames.indexOf(mVal);
|
|
212
|
+
let index = aliasIndex !== -1 ? aliasIndex : Number(val) - 1;
|
|
213
|
+
return config.nameMap[index] || val;
|
|
214
|
+
}
|
|
215
|
+
return val;
|
|
216
|
+
}
|
|
217
|
+
static describeField(value, config) {
|
|
218
|
+
if (value === "*" || value === "?")
|
|
219
|
+
return "";
|
|
220
|
+
const parts = value.split(",");
|
|
221
|
+
const descriptions = parts.map(part => {
|
|
222
|
+
if (part.includes("/")) {
|
|
223
|
+
const [start, step] = part.split("/");
|
|
224
|
+
const stepNum = Number(step);
|
|
225
|
+
const unit = stepNum === 1 ? config.unitSingular : config.unitPlural;
|
|
226
|
+
if (start === "*" || start === "0") {
|
|
227
|
+
return `every ${step} ${unit}`;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
const startDesc = this.resolveValue(start, config);
|
|
231
|
+
return `every ${step} ${unit} starting from ${startDesc}`;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (part.includes("-")) {
|
|
235
|
+
const [start, end] = part.split("-");
|
|
236
|
+
const startDesc = this.resolveValue(start, config);
|
|
237
|
+
const endDesc = this.resolveValue(end, config);
|
|
238
|
+
return `from ${startDesc} to ${endDesc}`;
|
|
239
|
+
}
|
|
240
|
+
return this.resolveValue(part, config);
|
|
241
|
+
});
|
|
242
|
+
if (descriptions.length === 1) {
|
|
243
|
+
return descriptions[0];
|
|
244
|
+
}
|
|
245
|
+
else if (descriptions.length === 2) {
|
|
246
|
+
return `${descriptions[0]} and ${descriptions[1]}`;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
return `${descriptions.slice(0, -1).join(", ")}, and ${descriptions[descriptions.length - 1]}`;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
static ordinalSuffix(i) {
|
|
253
|
+
if (isNaN(i) || i <= 0)
|
|
254
|
+
return "Invalid day";
|
|
255
|
+
const j = i % 10;
|
|
256
|
+
const k = i % 100;
|
|
257
|
+
if (j === 1 && k !== 11)
|
|
258
|
+
return i + "st";
|
|
259
|
+
if (j === 2 && k !== 12)
|
|
260
|
+
return i + "nd";
|
|
261
|
+
if (j === 3 && k !== 13)
|
|
262
|
+
return i + "rd";
|
|
263
|
+
return i + "th";
|
|
264
|
+
}
|
|
265
|
+
static combineDescriptions(descriptions) {
|
|
266
|
+
const filteredDescriptions = descriptions.filter((part) => part && part !== "");
|
|
267
|
+
return filteredDescriptions.length > 0
|
|
268
|
+
? filteredDescriptions.join(" ").trim()
|
|
269
|
+
: "Every moment";
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
exports.CronDescriberU2Q = CronDescriberU2Q;
|