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 CHANGED
@@ -1,123 +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
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
- // 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 * * *"
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
- // Get human-readable description
68
- const description = CronConverterU2Q.describeUnix('*/5 * * * *');
69
- console.log(description); // "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"
70
51
  ```
71
52
 
72
- ## 📚 Examples
73
-
74
- ### Basic Conversions
53
+ ### 2. Validation
54
+ Perform boundary checks, detect syntax errors, and validate range limits.
75
55
 
76
56
  ```typescript
77
- // Unix to Quartz
78
- CronConverterU2Q.unixToQuartz('0 12 * * *'); // "0 0 12 * * ? *"
79
- CronConverterU2Q.unixToQuartz('*/15 * * * *'); // "0 */15 * * * ? *"
57
+ import { CronValidatorU2Q } from 'cron-converter-u2q';
80
58
 
81
- // Quartz to Unix
82
- CronConverterU2Q.quartzToUnix('0 0 8 * * ?'); // "0 8 * * *"
83
- CronConverterU2Q.quartzToUnix('0 */5 * * * ?'); // "*/5 * * * *"
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-readable Descriptions
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
- // Unix format descriptions
90
- CronConverterU2Q.describeUnix('0 12 * * *'); // "At 12:00 PM"
91
- CronConverterU2Q.describeUnix('*/15 * * * *'); // "Every 15 minutes"
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 format descriptions
94
- CronConverterU2Q.describeQuartz('0 0 8 * * ?'); // "At 8:00 AM"
95
- CronConverterU2Q.describeQuartz('0 */5 * * * ?'); // "Every 5 minutes"
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 submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
87
+ Contributions are welcome! Please feel free to open issues or submit Pull Requests.
101
88
 
102
- 1. Fork the repository
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 - see the [LICENSE](LICENSE) file for details.
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.
@@ -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
- private static validateIfNullOrEmpty;
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
- this.validateIfNullOrEmpty(unixExpression);
12
- const parts = unixExpression.split(this.delimiter);
13
- if (parts.length !== this.unixExpressionLength)
14
- throw new Error(`Invalid unix cron format`);
15
- const [min, hour, dom, month, dow] = parts;
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
- let quartzDow = dow;
18
- if (dom === '*' && (dow === '*' || dow !== '*'))
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
- else if (dom !== '*' && dow === '*')
23
+ }
24
+ else if (dom !== '*' && dow === '*') {
21
25
  quartzDow = '?';
22
- return `0 ${min} ${hour} ${quartzDom} ${month} ${quartzDow}`;
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
- this.validateIfNullOrEmpty(quartzExpression);
31
- const parts = quartzExpression.split(this.delimiter);
32
- if (!this.quartzExpressionLengths.includes(parts.length))
33
- throw new Error(`Invalid quartz cron format`);
34
- const [_, min, hour, dom, month, dow] = parts;
35
- let unixDom = dom;
36
- let unixDow = dow;
37
- if (dom === '?' && dow === '*')
38
- unixDom = '*';
39
- else if (dow === '?' && dom === '*')
40
- unixDow = '*';
41
- else if (dom !== '?' && dom === '?')
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
- static validateIfNullOrEmpty(cronExpression) {
46
- if (!cronExpression || cronExpression.trim() === '')
47
- throw new Error('Empty or null expression');
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
+ }
@@ -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;