cronli5 0.7.2 → 0.8.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/CHANGELOG.md +57 -0
- package/README.md +30 -13
- package/cronli5.min.js +2 -2
- package/dist/cronli5.cjs +67 -7
- package/dist/cronli5.js +67 -7
- package/dist/lang/de.cjs +10 -9
- package/dist/lang/de.js +10 -9
- package/dist/lang/en.cjs +9 -4
- package/dist/lang/en.js +9 -4
- package/dist/lang/es.cjs +7 -3
- package/dist/lang/es.js +7 -3
- package/dist/lang/fi.cjs +11 -6
- package/dist/lang/fi.js +11 -6
- package/dist/lang/fr.cjs +7 -3
- package/dist/lang/fr.js +7 -3
- package/dist/lang/pt.cjs +7 -3
- package/dist/lang/pt.js +7 -3
- package/dist/lang/zh.cjs +29 -8
- package/dist/lang/zh.js +29 -8
- package/package.json +1 -1
- package/src/core/index.ts +7 -3
- package/src/core/quartz.ts +97 -0
- package/src/core/schedule.ts +1 -0
- package/src/core/specs.ts +2 -2
- package/src/cronli5.ts +20 -3
- package/src/lang/de/index.ts +41 -30
- package/src/lang/en/index.ts +25 -7
- package/src/lang/es/index.ts +23 -7
- package/src/lang/fi/index.ts +36 -18
- package/src/lang/fr/index.ts +23 -7
- package/src/lang/pt/index.ts +23 -6
- package/src/lang/zh/index.ts +81 -12
- package/src/types.ts +44 -0
- package/types/core/quartz.d.ts +4 -0
- package/types/core/schedule.d.ts +1 -0
- package/types/cronli5.d.ts +4 -4
- package/types/types.d.ts +39 -0
package/dist/lang/fr.js
CHANGED
|
@@ -193,6 +193,7 @@ function normalizeOptions(options) {
|
|
|
193
193
|
// satisfied without the 12-hour machinery the es donor carried.
|
|
194
194
|
ampm: false,
|
|
195
195
|
lenient: !!options.lenient,
|
|
196
|
+
quartz: !!options.quartz,
|
|
196
197
|
seconds: !!options.seconds,
|
|
197
198
|
short: !!options.short,
|
|
198
199
|
style,
|
|
@@ -338,6 +339,9 @@ function minutesList(schedule, opts) {
|
|
|
338
339
|
opts
|
|
339
340
|
) ?? "aux minutes " + joinList(segmentWords(segmentsOf(schedule, "minute"))) + " de chaque heure";
|
|
340
341
|
}
|
|
342
|
+
function withoutHourAnchor(lead) {
|
|
343
|
+
return lead.replace(/ de chaque heure$/, "");
|
|
344
|
+
}
|
|
341
345
|
function minuteRangeLead(minuteField) {
|
|
342
346
|
const bounds = minuteField.split("-");
|
|
343
347
|
return "chaque minute de " + bounds[0] + " \xE0 " + bounds[1];
|
|
@@ -375,7 +379,7 @@ function renderMinuteFrequency(schedule, plan, opts) {
|
|
|
375
379
|
} else if (plan.hours.kind === "window") {
|
|
376
380
|
phrase += " " + hourWindow(plan.hours, opts);
|
|
377
381
|
} else if (plan.hours.kind === "step") {
|
|
378
|
-
phrase
|
|
382
|
+
phrase = withoutHourAnchor(phrase) + ", " + stepHourSpan(stepSegment(schedule, "hour"), opts);
|
|
379
383
|
}
|
|
380
384
|
return phrase + trailingQualifier(schedule, opts);
|
|
381
385
|
}
|
|
@@ -412,7 +416,7 @@ function renderMinuteSpanAcrossHourStep(schedule, plan, opts) {
|
|
|
412
416
|
if (plan.form === "wildcard") {
|
|
413
417
|
return "chaque minute, " + stepHourSpan(segment, opts) + trailingQualifier(schedule, opts);
|
|
414
418
|
}
|
|
415
|
-
const lead = plan.form === "list" ? minutesList(schedule, opts) : minuteRangeLead(schedule.pattern.minute);
|
|
419
|
+
const lead = withoutHourAnchor(plan.form === "list" ? minutesList(schedule, opts) : minuteRangeLead(schedule.pattern.minute));
|
|
416
420
|
return lead + ", " + (cadence ?? stepHours(segment, opts)) + trailingQualifier(schedule, opts);
|
|
417
421
|
}
|
|
418
422
|
function renderEveryHour(schedule, plan, opts) {
|
|
@@ -585,7 +589,7 @@ function renderCompactClockTimes(schedule, plan, opts) {
|
|
|
585
589
|
);
|
|
586
590
|
}
|
|
587
591
|
const cadence = unevenHourCadence(schedule, opts);
|
|
588
|
-
const phrase = cadence ? minutesList(schedule, opts) + ", " + cadence + trailingQualifier(schedule, opts) : minutesList(schedule, opts) + ", " + hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
|
|
592
|
+
const phrase = cadence ? withoutHourAnchor(minutesList(schedule, opts)) + ", " + cadence + trailingQualifier(schedule, opts) : minutesList(schedule, opts) + ", " + hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
|
|
589
593
|
return schedule.analyses.clockSecond ? secondsLeadClause(schedule, opts) + ", " + phrase : phrase;
|
|
590
594
|
}
|
|
591
595
|
var renderers = {
|
package/dist/lang/pt.cjs
CHANGED
|
@@ -305,6 +305,7 @@ function normalizeOptions(options) {
|
|
|
305
305
|
// `{ampm}` option overrides it.
|
|
306
306
|
ampm: typeof options.ampm === "boolean" ? options.ampm : style.ampm,
|
|
307
307
|
lenient: !!options.lenient,
|
|
308
|
+
quartz: !!options.quartz,
|
|
308
309
|
seconds: !!options.seconds,
|
|
309
310
|
short: !!options.short,
|
|
310
311
|
style,
|
|
@@ -452,6 +453,9 @@ function minutesList(schedule, opts) {
|
|
|
452
453
|
opts
|
|
453
454
|
) ?? "nos minutos " + joinList(segmentWords(segmentsOf(schedule, "minute"))) + " de cada hora";
|
|
454
455
|
}
|
|
456
|
+
function withoutHourAnchor(lead) {
|
|
457
|
+
return lead.replace(/ de cada hora$/, "");
|
|
458
|
+
}
|
|
455
459
|
function minuteRangeLead(minuteField) {
|
|
456
460
|
const bounds = minuteField.split("-");
|
|
457
461
|
return "a cada minuto do " + bounds[0] + " ao " + bounds[1];
|
|
@@ -522,7 +526,7 @@ function renderMinuteFrequency(schedule, plan, opts) {
|
|
|
522
526
|
} else if (plan.hours.kind === "window") {
|
|
523
527
|
phrase += " " + hourWindow(plan.hours, opts);
|
|
524
528
|
} else if (plan.hours.kind === "step") {
|
|
525
|
-
phrase
|
|
529
|
+
phrase = withoutHourAnchor(phrase) + ", " + stepHourSpan(stepSegment(schedule, "hour"), opts);
|
|
526
530
|
}
|
|
527
531
|
return phrase + trailingQualifier(schedule, opts);
|
|
528
532
|
}
|
|
@@ -559,7 +563,7 @@ function renderMinuteSpanAcrossHourStep(schedule, plan, opts) {
|
|
|
559
563
|
if (plan.form === "wildcard") {
|
|
560
564
|
return "a cada minuto, " + stepHourSpan(segment, opts) + trailingQualifier(schedule, opts);
|
|
561
565
|
}
|
|
562
|
-
const lead = plan.form === "list" ? minutesList(schedule, opts) : minuteRangeLead(schedule.pattern.minute);
|
|
566
|
+
const lead = withoutHourAnchor(plan.form === "list" ? minutesList(schedule, opts) : minuteRangeLead(schedule.pattern.minute));
|
|
563
567
|
return lead + ", " + (cadence ?? stepHours(segment, opts)) + trailingQualifier(schedule, opts);
|
|
564
568
|
}
|
|
565
569
|
function renderEveryHour(schedule, plan, opts) {
|
|
@@ -864,7 +868,7 @@ function renderCompactClockTimes(schedule, plan, opts) {
|
|
|
864
868
|
);
|
|
865
869
|
}
|
|
866
870
|
const cadence = unevenHourCadence(schedule, opts);
|
|
867
|
-
const phrase = cadence ? minutesList(schedule, opts) + ", " + cadence + trailingQualifier(schedule, opts) : minutesList(schedule, opts) + ", " + hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
|
|
871
|
+
const phrase = cadence ? withoutHourAnchor(minutesList(schedule, opts)) + ", " + cadence + trailingQualifier(schedule, opts) : minutesList(schedule, opts) + ", " + hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
|
|
868
872
|
return schedule.analyses.clockSecond ? secondsLeadClause(schedule, opts) + ", " + phrase : phrase;
|
|
869
873
|
}
|
|
870
874
|
var renderers = {
|
package/dist/lang/pt.js
CHANGED
|
@@ -279,6 +279,7 @@ function normalizeOptions(options) {
|
|
|
279
279
|
// `{ampm}` option overrides it.
|
|
280
280
|
ampm: typeof options.ampm === "boolean" ? options.ampm : style.ampm,
|
|
281
281
|
lenient: !!options.lenient,
|
|
282
|
+
quartz: !!options.quartz,
|
|
282
283
|
seconds: !!options.seconds,
|
|
283
284
|
short: !!options.short,
|
|
284
285
|
style,
|
|
@@ -426,6 +427,9 @@ function minutesList(schedule, opts) {
|
|
|
426
427
|
opts
|
|
427
428
|
) ?? "nos minutos " + joinList(segmentWords(segmentsOf(schedule, "minute"))) + " de cada hora";
|
|
428
429
|
}
|
|
430
|
+
function withoutHourAnchor(lead) {
|
|
431
|
+
return lead.replace(/ de cada hora$/, "");
|
|
432
|
+
}
|
|
429
433
|
function minuteRangeLead(minuteField) {
|
|
430
434
|
const bounds = minuteField.split("-");
|
|
431
435
|
return "a cada minuto do " + bounds[0] + " ao " + bounds[1];
|
|
@@ -496,7 +500,7 @@ function renderMinuteFrequency(schedule, plan, opts) {
|
|
|
496
500
|
} else if (plan.hours.kind === "window") {
|
|
497
501
|
phrase += " " + hourWindow(plan.hours, opts);
|
|
498
502
|
} else if (plan.hours.kind === "step") {
|
|
499
|
-
phrase
|
|
503
|
+
phrase = withoutHourAnchor(phrase) + ", " + stepHourSpan(stepSegment(schedule, "hour"), opts);
|
|
500
504
|
}
|
|
501
505
|
return phrase + trailingQualifier(schedule, opts);
|
|
502
506
|
}
|
|
@@ -533,7 +537,7 @@ function renderMinuteSpanAcrossHourStep(schedule, plan, opts) {
|
|
|
533
537
|
if (plan.form === "wildcard") {
|
|
534
538
|
return "a cada minuto, " + stepHourSpan(segment, opts) + trailingQualifier(schedule, opts);
|
|
535
539
|
}
|
|
536
|
-
const lead = plan.form === "list" ? minutesList(schedule, opts) : minuteRangeLead(schedule.pattern.minute);
|
|
540
|
+
const lead = withoutHourAnchor(plan.form === "list" ? minutesList(schedule, opts) : minuteRangeLead(schedule.pattern.minute));
|
|
537
541
|
return lead + ", " + (cadence ?? stepHours(segment, opts)) + trailingQualifier(schedule, opts);
|
|
538
542
|
}
|
|
539
543
|
function renderEveryHour(schedule, plan, opts) {
|
|
@@ -838,7 +842,7 @@ function renderCompactClockTimes(schedule, plan, opts) {
|
|
|
838
842
|
);
|
|
839
843
|
}
|
|
840
844
|
const cadence = unevenHourCadence(schedule, opts);
|
|
841
|
-
const phrase = cadence ? minutesList(schedule, opts) + ", " + cadence + trailingQualifier(schedule, opts) : minutesList(schedule, opts) + ", " + hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
|
|
845
|
+
const phrase = cadence ? withoutHourAnchor(minutesList(schedule, opts)) + ", " + cadence + trailingQualifier(schedule, opts) : minutesList(schedule, opts) + ", " + hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
|
|
842
846
|
return schedule.analyses.clockSecond ? secondsLeadClause(schedule, opts) + ", " + phrase : phrase;
|
|
843
847
|
}
|
|
844
848
|
var renderers = {
|
package/dist/lang/zh.cjs
CHANGED
|
@@ -346,6 +346,9 @@ function minuteHourClause(schedule) {
|
|
|
346
346
|
function renderMinutePast(schedule) {
|
|
347
347
|
return minuteHourClause(schedule);
|
|
348
348
|
}
|
|
349
|
+
function withoutHourAnchor(clause) {
|
|
350
|
+
return clause.replace(/^每小时/, "");
|
|
351
|
+
}
|
|
349
352
|
function hourSegmentWords(segment) {
|
|
350
353
|
if (segment.kind === "range") {
|
|
351
354
|
return [hourWord(+segment.bounds[0]) + "\u81F3" + hourWord(+segment.bounds[1])];
|
|
@@ -373,7 +376,8 @@ function renderMinuteFrequency(schedule, plan) {
|
|
|
373
376
|
if (hours.kind === "step" || hours.kind === "during") {
|
|
374
377
|
const hourCad = unevenHourCadence(schedule);
|
|
375
378
|
if (hourCad !== null) {
|
|
376
|
-
|
|
379
|
+
const minuteBase = hours.kind === "step" ? withoutHourAnchor(base) : base;
|
|
380
|
+
return hourCad + (hourCad.indexOf("\u81F3") === -1 ? "" : "\uFF0C") + minuteBase;
|
|
377
381
|
}
|
|
378
382
|
}
|
|
379
383
|
if (hours.kind === "window" && hours.from === hours.to) {
|
|
@@ -406,9 +410,9 @@ function renderMinuteSpanAcrossHourStep(schedule, plan) {
|
|
|
406
410
|
const hourStep = stepSegment(schedule, "hour");
|
|
407
411
|
const { form } = plan;
|
|
408
412
|
if (form === "list") {
|
|
409
|
-
return hourCadencePhrase(schedule) + "\uFF0C" + renderMinutePast(schedule);
|
|
413
|
+
return hourCadencePhrase(schedule) + "\uFF0C" + withoutHourAnchor(renderMinutePast(schedule));
|
|
410
414
|
}
|
|
411
|
-
const minuteTail = form === "wildcard" ? "\u6BCF\u5206\u949F" : minuteHourClause(schedule) + "\uFF0C\u6BCF\u5206\u949F";
|
|
415
|
+
const minuteTail = form === "wildcard" ? "\u6BCF\u5206\u949F" : withoutHourAnchor(minuteHourClause(schedule)) + "\uFF0C\u6BCF\u5206\u949F";
|
|
412
416
|
if (hourStep.startToken !== "*") {
|
|
413
417
|
return hourCadencePhrase(schedule) + "\uFF0C" + minuteTail;
|
|
414
418
|
}
|
|
@@ -436,7 +440,7 @@ function renderCompactClockTimes(schedule, plan) {
|
|
|
436
440
|
const tail = secs.length && schedule.pattern.second !== "0" ? "\uFF0C\u7B2C" + valueText(secs) + "\u79D2" : "";
|
|
437
441
|
if (!compact.fold) {
|
|
438
442
|
const hourCad = unevenHourCadence(schedule);
|
|
439
|
-
return hourCad === null ? minuteHourClause(schedule) + "\uFF0C\u5728" + hourList(schedule) + tail : hourCad + "\uFF0C" + minuteHourClause(schedule) + tail;
|
|
443
|
+
return hourCad === null ? minuteHourClause(schedule) + "\uFF0C\u5728" + hourList(schedule) + tail : hourCad + "\uFF0C" + withoutHourAnchor(minuteHourClause(schedule)) + tail;
|
|
440
444
|
}
|
|
441
445
|
if (compact.minute > 0) {
|
|
442
446
|
return minuteHourClause(schedule) + "\uFF0C\u5728" + hourList(schedule) + tail;
|
|
@@ -599,6 +603,10 @@ function composeSecondsOnHour(schedule, plan, opts) {
|
|
|
599
603
|
if (composedClock && schedule.pattern.minute === "0") {
|
|
600
604
|
return composeMinuteZeroClocks(schedule, sec);
|
|
601
605
|
}
|
|
606
|
+
const fusedSingleMinute = composeSingleMinuteClocks(schedule, rest, sec, opts);
|
|
607
|
+
if (fusedSingleMinute !== null) {
|
|
608
|
+
return fusedSingleMinute;
|
|
609
|
+
}
|
|
602
610
|
const restText = render(schedule, rest, opts);
|
|
603
611
|
const secTail = clockRestCarriesSecond(rest) ? "" : sec;
|
|
604
612
|
if (composedClock && isDaily(schedule)) {
|
|
@@ -609,6 +617,13 @@ function composeSecondsOnHour(schedule, plan, opts) {
|
|
|
609
617
|
}
|
|
610
618
|
return restText + secTail;
|
|
611
619
|
}
|
|
620
|
+
function composeSingleMinuteClocks(schedule, rest, sec, opts) {
|
|
621
|
+
if (rest.kind !== "clockTimes" || schedule.shapes.minute !== "single" || clockRestCarriesSecond(rest)) {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
const core = render(schedule, rest, opts) + minuteZeroSecondTail(schedule, sec);
|
|
625
|
+
return isDaily(schedule) ? "\u6BCF\u5929" + core : core;
|
|
626
|
+
}
|
|
612
627
|
function composeMinuteZeroClocks(schedule, sec) {
|
|
613
628
|
if (hasHourWindow(schedule)) {
|
|
614
629
|
return isDaily(schedule) ? "\u6BCF\u5929" + hourRangeWindow(schedule, sec) : hourRangeWindow(schedule, sec);
|
|
@@ -616,11 +631,16 @@ function composeMinuteZeroClocks(schedule, sec) {
|
|
|
616
631
|
const clocks = hourFires(schedule).map(function clock(hour) {
|
|
617
632
|
return hour === 12 ? "\u6B63\u5348" : hourWord(hour) + "0\u5206";
|
|
618
633
|
});
|
|
619
|
-
const
|
|
620
|
-
const tail = sec === "\u6BCF\u79D2" ? "\u7684\u6BCF\u4E00\u79D2" : "\u7684" + (nested ?? sec);
|
|
621
|
-
const core = joinAnd(clocks) + tail;
|
|
634
|
+
const core = joinAnd(clocks) + minuteZeroSecondTail(schedule, sec);
|
|
622
635
|
return isDaily(schedule) ? "\u6BCF\u5929" + core : core;
|
|
623
636
|
}
|
|
637
|
+
function minuteZeroSecondTail(schedule, sec) {
|
|
638
|
+
if (sec === "\u6BCF\u79D2") {
|
|
639
|
+
return "\u7684\u6BCF\u4E00\u79D2";
|
|
640
|
+
}
|
|
641
|
+
const nested = strideFromSegments(segmentsOf(schedule, "second"), "\u79D2", "\u79D2", "");
|
|
642
|
+
return "\u7684" + (nested ?? sec);
|
|
643
|
+
}
|
|
624
644
|
function hasHourWindow(schedule) {
|
|
625
645
|
return segmentsOf(schedule, "hour").some(function range(segment) {
|
|
626
646
|
return segment.kind === "range";
|
|
@@ -667,7 +687,7 @@ function composeSecondsListed(schedule) {
|
|
|
667
687
|
}
|
|
668
688
|
const hourCad = unevenHourCadence(schedule);
|
|
669
689
|
if (hourCad !== null) {
|
|
670
|
-
return hourCad + "\uFF0C" + minutes + "\uFF0C" + sec;
|
|
690
|
+
return hourCad + "\uFF0C" + withoutHourAnchor(minutes) + "\uFF0C" + sec;
|
|
671
691
|
}
|
|
672
692
|
return hourFrame(schedule) + minutes + "\uFF0C" + sec;
|
|
673
693
|
}
|
|
@@ -945,6 +965,7 @@ function normalizeOptions(options) {
|
|
|
945
965
|
return {
|
|
946
966
|
ampm: typeof options.ampm === "boolean" ? options.ampm : false,
|
|
947
967
|
lenient: !!options.lenient,
|
|
968
|
+
quartz: !!options.quartz,
|
|
948
969
|
seconds: !!options.seconds,
|
|
949
970
|
short: !!options.short,
|
|
950
971
|
style,
|
package/dist/lang/zh.js
CHANGED
|
@@ -320,6 +320,9 @@ function minuteHourClause(schedule) {
|
|
|
320
320
|
function renderMinutePast(schedule) {
|
|
321
321
|
return minuteHourClause(schedule);
|
|
322
322
|
}
|
|
323
|
+
function withoutHourAnchor(clause) {
|
|
324
|
+
return clause.replace(/^每小时/, "");
|
|
325
|
+
}
|
|
323
326
|
function hourSegmentWords(segment) {
|
|
324
327
|
if (segment.kind === "range") {
|
|
325
328
|
return [hourWord(+segment.bounds[0]) + "\u81F3" + hourWord(+segment.bounds[1])];
|
|
@@ -347,7 +350,8 @@ function renderMinuteFrequency(schedule, plan) {
|
|
|
347
350
|
if (hours.kind === "step" || hours.kind === "during") {
|
|
348
351
|
const hourCad = unevenHourCadence(schedule);
|
|
349
352
|
if (hourCad !== null) {
|
|
350
|
-
|
|
353
|
+
const minuteBase = hours.kind === "step" ? withoutHourAnchor(base) : base;
|
|
354
|
+
return hourCad + (hourCad.indexOf("\u81F3") === -1 ? "" : "\uFF0C") + minuteBase;
|
|
351
355
|
}
|
|
352
356
|
}
|
|
353
357
|
if (hours.kind === "window" && hours.from === hours.to) {
|
|
@@ -380,9 +384,9 @@ function renderMinuteSpanAcrossHourStep(schedule, plan) {
|
|
|
380
384
|
const hourStep = stepSegment(schedule, "hour");
|
|
381
385
|
const { form } = plan;
|
|
382
386
|
if (form === "list") {
|
|
383
|
-
return hourCadencePhrase(schedule) + "\uFF0C" + renderMinutePast(schedule);
|
|
387
|
+
return hourCadencePhrase(schedule) + "\uFF0C" + withoutHourAnchor(renderMinutePast(schedule));
|
|
384
388
|
}
|
|
385
|
-
const minuteTail = form === "wildcard" ? "\u6BCF\u5206\u949F" : minuteHourClause(schedule) + "\uFF0C\u6BCF\u5206\u949F";
|
|
389
|
+
const minuteTail = form === "wildcard" ? "\u6BCF\u5206\u949F" : withoutHourAnchor(minuteHourClause(schedule)) + "\uFF0C\u6BCF\u5206\u949F";
|
|
386
390
|
if (hourStep.startToken !== "*") {
|
|
387
391
|
return hourCadencePhrase(schedule) + "\uFF0C" + minuteTail;
|
|
388
392
|
}
|
|
@@ -410,7 +414,7 @@ function renderCompactClockTimes(schedule, plan) {
|
|
|
410
414
|
const tail = secs.length && schedule.pattern.second !== "0" ? "\uFF0C\u7B2C" + valueText(secs) + "\u79D2" : "";
|
|
411
415
|
if (!compact.fold) {
|
|
412
416
|
const hourCad = unevenHourCadence(schedule);
|
|
413
|
-
return hourCad === null ? minuteHourClause(schedule) + "\uFF0C\u5728" + hourList(schedule) + tail : hourCad + "\uFF0C" + minuteHourClause(schedule) + tail;
|
|
417
|
+
return hourCad === null ? minuteHourClause(schedule) + "\uFF0C\u5728" + hourList(schedule) + tail : hourCad + "\uFF0C" + withoutHourAnchor(minuteHourClause(schedule)) + tail;
|
|
414
418
|
}
|
|
415
419
|
if (compact.minute > 0) {
|
|
416
420
|
return minuteHourClause(schedule) + "\uFF0C\u5728" + hourList(schedule) + tail;
|
|
@@ -573,6 +577,10 @@ function composeSecondsOnHour(schedule, plan, opts) {
|
|
|
573
577
|
if (composedClock && schedule.pattern.minute === "0") {
|
|
574
578
|
return composeMinuteZeroClocks(schedule, sec);
|
|
575
579
|
}
|
|
580
|
+
const fusedSingleMinute = composeSingleMinuteClocks(schedule, rest, sec, opts);
|
|
581
|
+
if (fusedSingleMinute !== null) {
|
|
582
|
+
return fusedSingleMinute;
|
|
583
|
+
}
|
|
576
584
|
const restText = render(schedule, rest, opts);
|
|
577
585
|
const secTail = clockRestCarriesSecond(rest) ? "" : sec;
|
|
578
586
|
if (composedClock && isDaily(schedule)) {
|
|
@@ -583,6 +591,13 @@ function composeSecondsOnHour(schedule, plan, opts) {
|
|
|
583
591
|
}
|
|
584
592
|
return restText + secTail;
|
|
585
593
|
}
|
|
594
|
+
function composeSingleMinuteClocks(schedule, rest, sec, opts) {
|
|
595
|
+
if (rest.kind !== "clockTimes" || schedule.shapes.minute !== "single" || clockRestCarriesSecond(rest)) {
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
const core = render(schedule, rest, opts) + minuteZeroSecondTail(schedule, sec);
|
|
599
|
+
return isDaily(schedule) ? "\u6BCF\u5929" + core : core;
|
|
600
|
+
}
|
|
586
601
|
function composeMinuteZeroClocks(schedule, sec) {
|
|
587
602
|
if (hasHourWindow(schedule)) {
|
|
588
603
|
return isDaily(schedule) ? "\u6BCF\u5929" + hourRangeWindow(schedule, sec) : hourRangeWindow(schedule, sec);
|
|
@@ -590,11 +605,16 @@ function composeMinuteZeroClocks(schedule, sec) {
|
|
|
590
605
|
const clocks = hourFires(schedule).map(function clock(hour) {
|
|
591
606
|
return hour === 12 ? "\u6B63\u5348" : hourWord(hour) + "0\u5206";
|
|
592
607
|
});
|
|
593
|
-
const
|
|
594
|
-
const tail = sec === "\u6BCF\u79D2" ? "\u7684\u6BCF\u4E00\u79D2" : "\u7684" + (nested ?? sec);
|
|
595
|
-
const core = joinAnd(clocks) + tail;
|
|
608
|
+
const core = joinAnd(clocks) + minuteZeroSecondTail(schedule, sec);
|
|
596
609
|
return isDaily(schedule) ? "\u6BCF\u5929" + core : core;
|
|
597
610
|
}
|
|
611
|
+
function minuteZeroSecondTail(schedule, sec) {
|
|
612
|
+
if (sec === "\u6BCF\u79D2") {
|
|
613
|
+
return "\u7684\u6BCF\u4E00\u79D2";
|
|
614
|
+
}
|
|
615
|
+
const nested = strideFromSegments(segmentsOf(schedule, "second"), "\u79D2", "\u79D2", "");
|
|
616
|
+
return "\u7684" + (nested ?? sec);
|
|
617
|
+
}
|
|
598
618
|
function hasHourWindow(schedule) {
|
|
599
619
|
return segmentsOf(schedule, "hour").some(function range(segment) {
|
|
600
620
|
return segment.kind === "range";
|
|
@@ -641,7 +661,7 @@ function composeSecondsListed(schedule) {
|
|
|
641
661
|
}
|
|
642
662
|
const hourCad = unevenHourCadence(schedule);
|
|
643
663
|
if (hourCad !== null) {
|
|
644
|
-
return hourCad + "\uFF0C" + minutes + "\uFF0C" + sec;
|
|
664
|
+
return hourCad + "\uFF0C" + withoutHourAnchor(minutes) + "\uFF0C" + sec;
|
|
645
665
|
}
|
|
646
666
|
return hourFrame(schedule) + minutes + "\uFF0C" + sec;
|
|
647
667
|
}
|
|
@@ -919,6 +939,7 @@ function normalizeOptions(options) {
|
|
|
919
939
|
return {
|
|
920
940
|
ampm: typeof options.ampm === "boolean" ? options.ampm : false,
|
|
921
941
|
lenient: !!options.lenient,
|
|
942
|
+
quartz: !!options.quartz,
|
|
922
943
|
seconds: !!options.seconds,
|
|
923
944
|
short: !!options.short,
|
|
924
945
|
style,
|
package/package.json
CHANGED
package/src/core/index.ts
CHANGED
|
@@ -5,16 +5,20 @@
|
|
|
5
5
|
|
|
6
6
|
import {applyQuartzAliases, normalizeCronPattern} from './normalize.js';
|
|
7
7
|
import type {NormalizedOptions, Pattern} from './schedule.js';
|
|
8
|
+
import {applyQuartz} from './quartz.js';
|
|
8
9
|
import {parseCronPattern} from './parse.js';
|
|
9
10
|
import type {CronPattern} from '../types.js';
|
|
10
11
|
import {validateCronPattern} from './validate.js';
|
|
11
12
|
|
|
12
|
-
// Parse, alias, validate, and normalize cron input
|
|
13
|
-
// cron-like object of string fields, ready for semantic
|
|
14
|
-
// rendering.
|
|
13
|
+
// Parse, apply Quartz semantics, alias, validate, and normalize cron input
|
|
14
|
+
// into a canonical cron-like object of string fields, ready for semantic
|
|
15
|
+
// analysis and rendering. Quartz handling runs first: it gates the `?` token
|
|
16
|
+
// (rejected unless `quartz`) and re-indexes the Quartz day-of-week to the
|
|
17
|
+
// canonical cron numbering the rest of the core expects.
|
|
15
18
|
function prepare(cronPattern: CronPattern, opts: NormalizedOptions): Pattern {
|
|
16
19
|
const pattern = parseCronPattern(cronPattern, opts);
|
|
17
20
|
|
|
21
|
+
applyQuartz(pattern, opts.quartz);
|
|
18
22
|
applyQuartzAliases(pattern);
|
|
19
23
|
validateCronPattern(pattern);
|
|
20
24
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Quartz input semantics. cronli5 accepts Quartz tokens (`?`, `L`, `W`, `#`),
|
|
2
|
+
// but Quartz numbers the day-of-week differently from standard cron: Quartz is
|
|
3
|
+
// 1 = Sunday … 7 = Saturday, while cron is 0/7 = Sunday, 1 = Monday. Reading a
|
|
4
|
+
// Quartz pattern with cron indexing silently shifts every weekday by one, so a
|
|
5
|
+
// Quartz `2` (Monday) would read as Tuesday. This module gates and re-indexes
|
|
6
|
+
// the input so the rest of the core keeps facing canonical cron values.
|
|
7
|
+
//
|
|
8
|
+
// The `?` token (Quartz's "no specific value", mandatory in Quartz, absent in
|
|
9
|
+
// standard cron) is the unambiguous mark of a Quartz pattern. Outside Quartz
|
|
10
|
+
// mode it is rejected outright rather than aliased to `*`, so a real Quartz
|
|
11
|
+
// cron errors loudly instead of being mis-read; inside Quartz mode it is the
|
|
12
|
+
// equivalent of `*`.
|
|
13
|
+
|
|
14
|
+
import type {CronLike} from './specs.js';
|
|
15
|
+
import {isNonNegativeInteger} from './util.js';
|
|
16
|
+
|
|
17
|
+
// The error a `?` raises outside Quartz mode: a clear pointer at the option
|
|
18
|
+
// that makes Quartz semantics (and `?`) available.
|
|
19
|
+
const quartzTokenMessage =
|
|
20
|
+
'`?` is a Quartz token — pass { quartz: true } to enable Quartz semantics.';
|
|
21
|
+
|
|
22
|
+
// In standard (non-Quartz) mode, `?` is not a valid value: reject it with a
|
|
23
|
+
// pointer at the `quartz` option. In Quartz mode, accept `?` as `*` and
|
|
24
|
+
// re-index the day-of-week from Quartz numbering (1 = Sunday) to the canonical
|
|
25
|
+
// cron numbering (0 = Sunday) the rest of the core expects. Operates in place
|
|
26
|
+
// on the raw cron-like object, before aliasing and validation.
|
|
27
|
+
function applyQuartz(cronPattern: CronLike, quartz: boolean): void {
|
|
28
|
+
if (!quartz) {
|
|
29
|
+
rejectQuartzToken(cronPattern.date);
|
|
30
|
+
rejectQuartzToken(cronPattern.weekday);
|
|
31
|
+
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if ('' + cronPattern.date === '?') {
|
|
36
|
+
cronPattern.date = '*';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if ('' + cronPattern.weekday === '?') {
|
|
40
|
+
cronPattern.weekday = '*';
|
|
41
|
+
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
cronPattern.weekday = reindexWeekday('' + cronPattern.weekday);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Throw the Quartz-token error if a field is exactly `?`.
|
|
49
|
+
function rejectQuartzToken(value: string | number): void {
|
|
50
|
+
if ('' + value === '?') {
|
|
51
|
+
throw new Error(quartzTokenMessage);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Re-index a Quartz day-of-week field to canonical cron numbering. Every
|
|
56
|
+
// numeric weekday position maps n -> n-1 (Quartz 1 = Sunday becomes cron 0),
|
|
57
|
+
// across singles, ranges, and step bounds, and inside the DOW operators `nL`
|
|
58
|
+
// (last weekday) and `n#k` (kth weekday). Day NAMES (`MON`) are unambiguous and
|
|
59
|
+
// left untouched, as is the bare `L` alias (Saturday in both numberings) and
|
|
60
|
+
// `*`. Quartz has no weekday 0; it is rejected here.
|
|
61
|
+
function reindexWeekday(value: string): string {
|
|
62
|
+
if (value === '*') {
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return value.split(',').map(reindexSegment).join(',');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Re-index one comma-separated weekday segment: a single, a range, or either
|
|
70
|
+
// of those followed by a `#k` or `L` operator (or a `/step`).
|
|
71
|
+
function reindexSegment(segment: string): string {
|
|
72
|
+
const operator = (/(#\d+|L)$/).exec(segment);
|
|
73
|
+
const suffix = operator ? operator[0] : '';
|
|
74
|
+
const core = suffix ? segment.slice(0, -suffix.length) : segment;
|
|
75
|
+
const step = core.split('/');
|
|
76
|
+
const range = step[0].split('-').map(reindexNumber).join('-');
|
|
77
|
+
const head = step.length === 2 ? range + '/' + step[1] : range;
|
|
78
|
+
|
|
79
|
+
return head + suffix;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Re-index a single weekday token: a number maps n -> n-1 (rejecting 0, which
|
|
83
|
+
// Quartz does not use); a name passes through unchanged.
|
|
84
|
+
function reindexNumber(token: string): string {
|
|
85
|
+
if (!isNonNegativeInteger(token)) {
|
|
86
|
+
return token;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (token === '0') {
|
|
90
|
+
throw new Error('`cronli5` was passed an invalid Quartz day-of-week ' +
|
|
91
|
+
'value "0"; Quartz numbers weekdays 1 (Sunday) through 7 (Saturday).');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return '' + (+token - 1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export {applyQuartz, quartzTokenMessage};
|
package/src/core/schedule.ts
CHANGED
package/src/core/specs.ts
CHANGED
|
@@ -55,9 +55,9 @@ const fieldSpecs: Record<Field, FieldSpec> = {
|
|
|
55
55
|
second: {cyclic: true, max: 59, min: 0, top: 59},
|
|
56
56
|
minute: {cyclic: true, max: 59, min: 0, top: 59},
|
|
57
57
|
hour: {cyclic: true, max: 23, min: 0, top: 23},
|
|
58
|
-
date: {
|
|
58
|
+
date: {cyclic: true, max: 31, min: 1, top: 31},
|
|
59
59
|
month: {cyclic: true, max: 12, min: 1, numbers: monthNumbers, top: 12},
|
|
60
|
-
weekday: {aliases: {
|
|
60
|
+
weekday: {aliases: {L: '6'}, cyclic: true, max: 7, min: 0,
|
|
61
61
|
numbers: weekdayNumbers, top: 6},
|
|
62
62
|
year: {max: 9999, min: 1970}
|
|
63
63
|
};
|
package/src/cronli5.ts
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
|
|
30
30
|
import {analyze, prepare} from './core/index.js';
|
|
31
31
|
import type {NormalizedOptions} from './core/schedule.js';
|
|
32
|
-
import type {CronPattern, Cronli5Language, Cronli5Options}
|
|
32
|
+
import type {Cronli5, CronPattern, Cronli5Language, Cronli5Options}
|
|
33
33
|
from './types.js';
|
|
34
34
|
import en from './lang/en/index.js';
|
|
35
35
|
|
|
@@ -88,8 +88,25 @@ function interpretCronPattern(
|
|
|
88
88
|
return lang.describe({...schedule, plan}, opts);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
export
|
|
91
|
+
// Two named convenience methods are attached to the callable export as sugar
|
|
92
|
+
// over the `sentence` option: `.sentence(...)` forces the capitalized
|
|
93
|
+
// standalone, `.fragment(...)` forces the embeddable fragment (the default).
|
|
94
|
+
// The method's own intent wins, so a passed-through `sentence` flag is
|
|
95
|
+
// overridden. There is no `toString` method on purpose — it would shadow
|
|
96
|
+
// `Function.prototype.toString` (called arg-less by `String()`, template
|
|
97
|
+
// literals, and console/debug) and break coercion; named methods sidestep that.
|
|
98
|
+
function sentence(cronPattern: CronPattern, options?: Cronli5Options): string {
|
|
99
|
+
return cronli5(cronPattern, {...options, sentence: true});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function fragment(cronPattern: CronPattern, options?: Cronli5Options): string {
|
|
103
|
+
return cronli5(cronPattern, {...options, sentence: false});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const callable: Cronli5 = Object.assign(cronli5, {sentence, fragment});
|
|
107
|
+
|
|
108
|
+
export default callable;
|
|
92
109
|
export type {
|
|
93
|
-
Cronli5Dialect, Cronli5Language, Cronli5Options, CronPattern,
|
|
110
|
+
Cronli5, Cronli5Dialect, Cronli5Language, Cronli5Options, CronPattern,
|
|
94
111
|
CronPatternObject
|
|
95
112
|
} from './types.js';
|