cron-converter-u2q 1.3.0 → 1.3.2

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 CHANGED
@@ -1,131 +1,93 @@
1
1
  # cron-converter-u2q
2
2
 
3
- [![Github Repo Stars](https://img.shields.io/github/stars/rahu619/cron-converter-u2q?style=social)](https://github.com/rahu619/cron-converter-u2q)
4
3
  [![NPM version](https://img.shields.io/npm/v/cron-converter-u2q)](https://www.npmjs.com/package/cron-converter-u2q)
4
+ [![npm downloads](https://img.shields.io/npm/dm/cron-converter-u2q)](https://www.npmjs.com/package/cron-converter-u2q)
5
5
  [![GitHub License](https://img.shields.io/github/license/rahu619/cron-converter-u2q?style=plastic)](LICENSE)
6
6
  [![GitHub Build](https://github.com/rahu619/cron-converter-u2q/actions/workflows/integration.yml/badge.svg?branch=main)](https://github.com/rahu619/cron-converter-u2q/actions)
7
- [![Github Release](https://github.com/rahu619/cron-converter-u2q/actions/workflows/release.yml/badge.svg?event=workflow_dispatch)](https://github.com/rahu619/cron-converter-u2q/actions)
8
- [![Github Top Language](https://img.shields.io/github/languages/top/rahu619/cron-converter-u2q?style=plastic)](https://www.typescriptlang.org/)
9
- [![npm downloads](https://img.shields.io/npm/dm/cron-converter-u2q)](https://www.npmjs.com/package/cron-converter-u2q)
10
-
11
- [![https://nodei.co/npm/cron-converter-u2q.png?downloads=true&downloadRank=true&stars=true](https://nodei.co/npm/cron-converter-u2q.png?downloads=true&downloadRank=true&stars=true)](https://www.npmjs.com/package/cron-converter-u2q)
12
7
 
13
- A powerful TypeScript library for working with cron expressions. Effortlessly convert between Unix and Quartz formats, generate human-readable descriptions, and validate cron expressions with ease.
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
- ## ✨ Features
10
+ ---
16
11
 
17
- ### 🔄 Two-way Conversion
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
- ### 📝 Human-readable Descriptions
24
- - **Natural Language**: Convert cron expressions to plain English
25
- - **Multiple Languages**: Support for different language descriptions
26
- - **Customizable**: Extend with your own description templates
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
- ### 🛠️ Developer Friendly
30
- - **TypeScript Support**: Full type definitions included
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
- ### 🔍 Validation & Error Handling
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
- ## 🚀 Quick Start
55
-
56
- ```typescript
57
- import { CronConverterU2Q, CronDescriberU2Q } from 'cron-converter-u2q';
58
-
59
- // Convert Unix to Quartz
60
- const quartzExpression = CronConverterU2Q.unixToQuartz('5 * * * *');
61
- console.log(quartzExpression); // "0 5 * * * ? *"
62
-
63
- // Convert Quartz to Unix
64
- const unixExpression = CronConverterU2Q.quartzToUnix('0 0 8 * * ?');
65
- console.log(unixExpression); // "0 8 * * *"
66
- // Get human-readable description
67
- const description = CronDescriberU2Q.describeUnix('*/5 * * * *');
68
- console.log(description); // "Every 5 minutes"
69
- ```
34
+ ---
70
35
 
71
- ## 📚 Examples
36
+ ## 🚀 Usage
72
37
 
73
- ### Basic Conversions
38
+ ### 1. Two-Way Conversion
39
+ Convert seamlessly between formats. Wildcards and aliases are normalized automatically.
74
40
 
75
41
  ```typescript
76
- // Unix to Quartz
77
- CronConverterU2Q.unixToQuartz('0 12 * * *'); // "0 0 12 * * ? *"
78
- CronConverterU2Q.unixToQuartz('*/15 * * * *'); // "0 */15 * * * ? *"
79
- // Quartz to Unix
80
- CronConverterU2Q.quartzToUnix('0 0 8 * * ?'); // "0 8 * * *"
81
- CronConverterU2Q.quartzToUnix('0 */5 * * * ?'); // "*/5 * * * *"
82
- ```
42
+ import { CronConverterU2Q } from 'cron-converter-u2q';
83
43
 
84
- ### Human-readable Descriptions
44
+ // Unix -> Quartz (adds 0 seconds)
45
+ CronConverterU2Q.unixToQuartz('*/15 * * * *'); // "0 */15 * * * * *"
46
+ CronConverterU2Q.unixToQuartz('0 12 * * 1'); // "0 0 12 ? * 2 *"
85
47
 
86
- ```typescript
87
- // Unix format descriptions
88
- CronDescriberU2Q.describeUnix('0 12 * * *'); // "At 12 o'clock"
89
- CronDescriberU2Q.describeUnix('*/15 * * * *'); // "Every 15 minutes"
90
-
91
- // Quartz format descriptions
92
- CronDescriberU2Q.describeQuartz('0 0 8 * * ?'); // "At 8 o'clock"
93
- CronDescriberU2Q.describeQuartz('0 */5 * * * ?'); // "Every 5 minutes"
48
+ // Quartz -> Unix (drops seconds/year)
49
+ CronConverterU2Q.quartzToUnix('0 0 8 * * ?'); // "0 8 * * *"
50
+ CronConverterU2Q.quartzToUnix('0 */5 * ? * 2'); // "*/5 * * * 1"
94
51
  ```
95
52
 
96
- ## 🤝 Contributing
53
+ ### 2. Validation
54
+ Perform boundary checks, detect syntax errors, and validate range limits.
97
55
 
98
- Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
56
+ ```typescript
57
+ import { CronValidatorU2Q } from 'cron-converter-u2q';
99
58
 
100
- 1. Fork the repository
101
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
102
- 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
103
- 4. Push to the branch (`git push origin feature/amazing-feature`)
104
- 5. Open a Pull Request
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"
105
62
 
106
- ## 📄 License
63
+ // Boolean check
64
+ CronValidatorU2Q.isValidUnix('*/5 * * * *'); // true
65
+ CronValidatorU2Q.isValidQuartz('0 0 12 * * * *'); // true (both are allowed to be '*' in this package)
66
+ ```
107
67
 
108
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
68
+ ### 3. Human-Readable Descriptions
69
+ Render cron schedules into clear, natural English. Fully supports lists, ranges, step modifiers, and Quartz special symbols.
109
70
 
110
- ## 💬 Support
71
+ ```typescript
72
+ import { CronDescriberU2Q } from 'cron-converter-u2q';
111
73
 
112
- - 📧 Email: rahu619@gmail.com
113
- - 💻 GitHub Issues: [Create an issue](https://github.com/rahu619/cron-converter-u2q/issues)
114
- - Star the repository if you find it useful!
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"
115
77
 
116
- ## 🙏 Acknowledgments
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"
81
+ ```
117
82
 
118
- - Thanks to all contributors who have helped shape this project
119
- - Inspired by the need for a simple, reliable cron expression converter
120
- - Built with TypeScript for better developer experience
83
+ ---
121
84
 
122
- ## 📖 Specifications
85
+ ## 🤝 Contributing
123
86
 
124
- This library's conversion logic is grounded in the following official specifications:
87
+ Contributions are welcome! Please feel free to open issues or submit Pull Requests.
125
88
 
126
- - **POSIX IEEE Std 1003.1** – Defines the standard Unix cron expression format (5 fields: minute, hour, day-of-month, month, day-of-week).
127
- [https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html)
89
+ ---
128
90
 
129
- - **Quartz Scheduler** – Defines the extended Quartz cron trigger format (6–7 fields: seconds, minute, hour, day-of-month, month, day-of-week, optional year). Notably, exactly one of `day-of-month` or `day-of-week` must be `?` to avoid scheduling conflicts.
130
- [https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html](https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html)
91
+ ## 📄 License
131
92
 
93
+ This project is licensed under the MIT License.
@@ -4,18 +4,13 @@ export declare class CronConverterU2Q {
4
4
  * @param unixExpression - the unix expression
5
5
  * @returns the corresponding quartz expression
6
6
  */
7
- static unixToQuartz(unixExpression: string): string;
7
+ static unixToQuartz(unixExpression: string, year?: string): string;
8
8
  /**
9
9
  * Converts a quartz cron expression to a unix cron expression
10
10
  * @param quartzExpression - the quartz expression
11
11
  * @returns the corresponding unix expression
12
12
  */
13
13
  static quartzToUnix(quartzExpression: string): string;
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
14
  /**
20
15
  * Converts Unix DOW to Quartz DOW, supporting lists, ranges, and special cases.
21
16
  * Unix: 0=Sun, 1=Mon, ..., 6=Sat, 7=Sun(alias)
package/lib/converter.js CHANGED
@@ -9,10 +9,10 @@ class CronConverterU2Q {
9
9
  * @param unixExpression - the unix expression
10
10
  * @returns the corresponding quartz expression
11
11
  */
12
- static unixToQuartz(unixExpression) {
12
+ static unixToQuartz(unixExpression, year = '*') {
13
13
  validator_1.CronValidatorU2Q.validateUnix(unixExpression);
14
14
  const parts = helper_1.ExpressionHelper.GetExpressionParts(unixExpression);
15
- const [min, hour, dom, month, dow] = parts.map(part => this.convertIntervalParts(part));
15
+ const [min, hour, dom, month, dow] = parts;
16
16
  // Enhanced DOW conversion: handle lists, ranges, and special cases
17
17
  let quartzDow = this.unixDowToQuartz(dow);
18
18
  let quartzDom = dom;
@@ -24,7 +24,14 @@ class CronConverterU2Q {
24
24
  else if (dom !== '*' && dow === '*') {
25
25
  quartzDow = '?';
26
26
  }
27
- return `0 ${min} ${hour} ${quartzDom} ${month} ${quartzDow} *`;
27
+ else if (dom !== '*' && dow !== '*') {
28
+ throw new Error("Quartz cron does not support specifying both Day of Month and Day of Week");
29
+ }
30
+ const result = `0 ${min} ${hour} ${quartzDom} ${month} ${quartzDow} ${year}`;
31
+ if (year !== '*') {
32
+ validator_1.CronValidatorU2Q.validateQuartz(result);
33
+ }
34
+ return result;
28
35
  }
29
36
  /**
30
37
  * Converts a quartz cron expression to a unix cron expression
@@ -34,7 +41,7 @@ class CronConverterU2Q {
34
41
  static quartzToUnix(quartzExpression) {
35
42
  validator_1.CronValidatorU2Q.validateQuartz(quartzExpression);
36
43
  const parts = helper_1.ExpressionHelper.GetExpressionParts(quartzExpression);
37
- const [_, min, hour, dom, month, dow] = parts.map(part => this.convertIntervalParts(part, true));
44
+ const [_, min, hour, dom, month, dow] = parts.map(part => part.replace(/^0\/(\d+)$/, '*/$1'));
38
45
  if (dom.includes('L') || dom.includes('W')) {
39
46
  throw new Error("Unix cron does not support 'L' or 'W' in Day of Month");
40
47
  }
@@ -46,16 +53,6 @@ class CronConverterU2Q {
46
53
  let unixDom = dom === '?' ? '*' : dom;
47
54
  return `${min} ${hour} ${unixDom} ${month} ${unixDow}`;
48
55
  }
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
56
  /**
60
57
  * Converts Unix DOW to Quartz DOW, supporting lists, ranges, and special cases.
61
58
  * Unix: 0=Sun, 1=Mon, ..., 6=Sat, 7=Sun(alias)
@@ -64,8 +61,10 @@ class CronConverterU2Q {
64
61
  static unixDowToQuartz(dow) {
65
62
  if (dow === '*' || dow === '?')
66
63
  return dow;
67
- if (dow.includes(','))
68
- return dow.split(',').map(d => this.unixDowToQuartz(d)).join(',');
64
+ if (dow.includes(',')) {
65
+ const mapped = dow.split(',').map(d => this.unixDowToQuartz(d));
66
+ return Array.from(new Set(mapped)).join(',');
67
+ }
69
68
  if (dow.includes('-'))
70
69
  return dow.split('-').map(d => this.unixDowToQuartz(d)).join('-');
71
70
  if (dow.endsWith('L')) {
@@ -92,8 +91,10 @@ class CronConverterU2Q {
92
91
  if (dow === '*' || dow === '?')
93
92
  return dow === '?' ? '*' : dow;
94
93
  // Split compound expressions so each element is converted individually
95
- if (dow.includes(','))
96
- return dow.split(',').map(d => this.quartzDowToUnix(d)).join(',');
94
+ if (dow.includes(',')) {
95
+ const mapped = dow.split(',').map(d => this.quartzDowToUnix(d));
96
+ return Array.from(new Set(mapped)).join(',');
97
+ }
97
98
  if (dow.includes('-'))
98
99
  return dow.split('-').map(d => this.quartzDowToUnix(d)).join('-');
99
100
  // Last (L) — convert the numeric day part, preserve L suffix
package/lib/helper.js CHANGED
@@ -4,10 +4,10 @@ exports.ExpressionHelper = void 0;
4
4
  class ExpressionHelper {
5
5
  static GetExpressionParts(expression) {
6
6
  this.validateIfNullOrEmpty(expression);
7
- const parts = expression.split(this.delimiter).map(part => part.trim());
7
+ const parts = expression.trim().split(/\s+/);
8
8
  if (this.quartzExpressionLengths.includes(parts.length))
9
9
  return parts;
10
- if (this.unixExpressionLength == parts.length)
10
+ if (this.unixExpressionLength === parts.length)
11
11
  return parts;
12
12
  throw new Error(`Invalid cron expression!`);
13
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cron-converter-u2q",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Converts cron expressions between unix and quartz formats",
5
5
  "main": "lib/index.js",
6
6
  "types": "types/index.d.ts",
@@ -290,6 +290,49 @@ describe("Converter: error handling", () => {
290
290
  test("quartzToUnix throws on 8-field expression", () => {
291
291
  expect(() => converter.quartzToUnix("* * * * * * * *")).toThrow();
292
292
  });
293
+
294
+ test("unixToQuartz throws when both DOM and DOW are specific", () => {
295
+ expect(() => converter.unixToQuartz("0 0 15 * 1")).toThrow("Quartz cron does not support specifying both Day of Month and Day of Week");
296
+ });
297
+ });
298
+
299
+ describe("Converter: deduplication of DOW values", () => {
300
+ test("unixToQuartz deduplicates redundant DOW values (e.g. 0,7)", () => {
301
+ expect(converter.unixToQuartz("0 0 * * 0,7")).toBe("0 0 0 ? * 1 *");
302
+ });
303
+
304
+ test("quartzToUnix deduplicates redundant DOW values (e.g. 2,2)", () => {
305
+ expect(converter.quartzToUnix("0 0 0 ? * 2,2")).toBe("0 0 * * 1");
306
+ });
307
+ });
308
+
309
+ describe("ExpressionHelper whitespace robustness", () => {
310
+ test("handles multiple spaces, tabs, and leading/trailing whitespace correctly", () => {
311
+ expect(converter.unixToQuartz(" 0 12 * * * ")).toBe("0 0 12 * * * *");
312
+ expect(converter.unixToQuartz("0\t12\t*\t*\t*")).toBe("0 0 12 * * * *");
313
+ });
314
+ });
315
+
316
+ describe("Converter: optional year parameter", () => {
317
+ test("unixToQuartz accepts specific year", () => {
318
+ expect(converter.unixToQuartz("0 12 * * 1", "2026")).toBe("0 0 12 ? * 2 2026");
319
+ });
320
+
321
+ test("unixToQuartz throws on invalid specific year", () => {
322
+ expect(() => converter.unixToQuartz("0 12 * * 1", "invalid_year")).toThrow();
323
+ expect(() => converter.unixToQuartz("0 12 * * 1", "1969")).toThrow(); // out of range
324
+ });
325
+
326
+ test("round-tripping Quartz -> Unix -> Quartz with specific years", () => {
327
+ const originalQuartz = "0 0 12 ? * 2 2026";
328
+ const unix = converter.quartzToUnix(originalQuartz); // "0 12 * * 1"
329
+
330
+ const parts = originalQuartz.split(/\s+/);
331
+ const year = parts.length === 7 ? parts[6] : "*";
332
+
333
+ const reconstructedQuartz = converter.unixToQuartz(unix, year);
334
+ expect(reconstructedQuartz).toBe(originalQuartz);
335
+ });
293
336
  });
294
337
 
295
338
  describe("Validator Checks", () => {
package/src/converter.ts CHANGED
@@ -7,10 +7,10 @@ export class CronConverterU2Q {
7
7
  * @param unixExpression - the unix expression
8
8
  * @returns the corresponding quartz expression
9
9
  */
10
- public static unixToQuartz(unixExpression: string): string {
10
+ public static unixToQuartz(unixExpression: string, year = '*'): string {
11
11
  CronValidatorU2Q.validateUnix(unixExpression);
12
12
  const parts = helper.GetExpressionParts(unixExpression);
13
- const [min, hour, dom, month, dow] = parts.map(part => this.convertIntervalParts(part));
13
+ const [min, hour, dom, month, dow] = parts;
14
14
 
15
15
  // Enhanced DOW conversion: handle lists, ranges, and special cases
16
16
  let quartzDow = this.unixDowToQuartz(dow);
@@ -22,9 +22,15 @@ export class CronConverterU2Q {
22
22
  quartzDom = '?';
23
23
  } else if (dom !== '*' && dow === '*') {
24
24
  quartzDow = '?';
25
+ } else if (dom !== '*' && dow !== '*') {
26
+ throw new Error("Quartz cron does not support specifying both Day of Month and Day of Week");
25
27
  }
26
28
 
27
- return `0 ${min} ${hour} ${quartzDom} ${month} ${quartzDow} *`;
29
+ const result = `0 ${min} ${hour} ${quartzDom} ${month} ${quartzDow} ${year}`;
30
+ if (year !== '*') {
31
+ CronValidatorU2Q.validateQuartz(result);
32
+ }
33
+ return result;
28
34
  }
29
35
 
30
36
  /**
@@ -35,7 +41,7 @@ export class CronConverterU2Q {
35
41
  public static quartzToUnix(quartzExpression: string): string {
36
42
  CronValidatorU2Q.validateQuartz(quartzExpression);
37
43
  const parts = helper.GetExpressionParts(quartzExpression);
38
- const [_, min, hour, dom, month, dow] = parts.map(part => this.convertIntervalParts(part, true));
44
+ const [_, min, hour, dom, month, dow] = parts.map(part => part.replace(/^0\/(\d+)$/, '*/$1'));
39
45
 
40
46
  if (dom.includes('L') || dom.includes('W')) {
41
47
  throw new Error("Unix cron does not support 'L' or 'W' in Day of Month");
@@ -51,17 +57,6 @@ export class CronConverterU2Q {
51
57
  return `${min} ${hour} ${unixDom} ${month} ${unixDow}`;
52
58
  }
53
59
 
54
- /**
55
- * Converts interval parts for both Unix and Quartz expressions.
56
- * In both directions, normalises step notation to the Unix `*\/N` format.
57
- */
58
- private static convertIntervalParts(part: string, isQuartz = false): string {
59
- if (isQuartz) {
60
- return part.replace(/^0\/(\d+)$/, '*/$1');
61
- }
62
- return part;
63
- }
64
-
65
60
  /**
66
61
  * Converts Unix DOW to Quartz DOW, supporting lists, ranges, and special cases.
67
62
  * Unix: 0=Sun, 1=Mon, ..., 6=Sat, 7=Sun(alias)
@@ -69,7 +64,10 @@ export class CronConverterU2Q {
69
64
  */
70
65
  private static unixDowToQuartz(dow: string): string {
71
66
  if (dow === '*' || dow === '?') return dow;
72
- if (dow.includes(',')) return dow.split(',').map(d => this.unixDowToQuartz(d)).join(',');
67
+ if (dow.includes(',')) {
68
+ const mapped = dow.split(',').map(d => this.unixDowToQuartz(d));
69
+ return Array.from(new Set(mapped)).join(',');
70
+ }
73
71
  if (dow.includes('-')) return dow.split('-').map(d => this.unixDowToQuartz(d)).join('-');
74
72
  if (dow.endsWith('L')) {
75
73
  const day = dow.slice(0, -1);
@@ -94,7 +92,10 @@ export class CronConverterU2Q {
94
92
  if (dow === '*' || dow === '?') return dow === '?' ? '*' : dow;
95
93
 
96
94
  // Split compound expressions so each element is converted individually
97
- if (dow.includes(',')) return dow.split(',').map(d => this.quartzDowToUnix(d)).join(',');
95
+ if (dow.includes(',')) {
96
+ const mapped = dow.split(',').map(d => this.quartzDowToUnix(d));
97
+ return Array.from(new Set(mapped)).join(',');
98
+ }
98
99
  if (dow.includes('-')) return dow.split('-').map(d => this.quartzDowToUnix(d)).join('-');
99
100
 
100
101
  // Last (L) — convert the numeric day part, preserve L suffix
package/src/helper.ts CHANGED
@@ -5,9 +5,9 @@ export class ExpressionHelper {
5
5
 
6
6
  public static GetExpressionParts(expression: string): string[] {
7
7
  this.validateIfNullOrEmpty(expression);
8
- const parts = expression.split(this.delimiter).map(part => part.trim());
8
+ const parts = expression.trim().split(/\s+/);
9
9
  if (this.quartzExpressionLengths.includes(parts.length)) return parts;
10
- if (this.unixExpressionLength == parts.length) return parts;
10
+ if (this.unixExpressionLength === parts.length) return parts;
11
11
 
12
12
  throw new Error(`Invalid cron expression!`)
13
13
  }
package/types/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  declare module 'cron-converter-u2q' {
2
2
  export class CronConverterU2Q {
3
- static unixToQuartz(unixExpression: string): string;
3
+ static unixToQuartz(unixExpression: string, year?: string): string;
4
4
  static quartzToUnix(quartzExpression: string): string;
5
5
  }
6
6