cronli5 0.1.5 → 0.1.7
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 +71 -0
- package/README.md +2 -2
- package/cronli5.min.js +2 -2
- package/dist/cronli5.cjs +156 -27
- package/dist/cronli5.js +156 -27
- package/dist/lang/de.cjs +157 -36
- package/dist/lang/de.js +157 -36
- package/dist/lang/en.cjs +156 -27
- package/dist/lang/en.js +156 -27
- package/dist/lang/es.cjs +156 -18
- package/dist/lang/es.js +156 -18
- package/dist/lang/fi.cjs +128 -20
- package/dist/lang/fi.js +128 -20
- package/dist/lang/zh.cjs +126 -58
- package/dist/lang/zh.js +126 -58
- package/package.json +2 -2
- package/src/core/util.ts +52 -1
- package/src/lang/de/index.ts +331 -74
- package/src/lang/en/index.ts +327 -62
- package/src/lang/es/index.ts +306 -39
- package/src/lang/fi/index.ts +251 -41
- package/src/lang/zh/index.ts +246 -105
- package/types/core/util.d.ts +10 -1
package/dist/lang/en.cjs
CHANGED
|
@@ -40,6 +40,26 @@ function arithmeticStep(values) {
|
|
|
40
40
|
}
|
|
41
41
|
return { start: values[0], interval, last: values[values.length - 1] };
|
|
42
42
|
}
|
|
43
|
+
function weekdayDisplayKey(value) {
|
|
44
|
+
return value === 0 ? 7 : value;
|
|
45
|
+
}
|
|
46
|
+
function orderWeekdaysForDisplay(segments) {
|
|
47
|
+
const flattened = segments.flatMap(function flat(segment) {
|
|
48
|
+
return segment.kind === "step" ? segment.fires.map(function single(value) {
|
|
49
|
+
return { kind: "single", value: "" + value };
|
|
50
|
+
}) : [segment];
|
|
51
|
+
});
|
|
52
|
+
function key(segment) {
|
|
53
|
+
return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
|
|
54
|
+
}
|
|
55
|
+
return flattened.map(function index(segment, position) {
|
|
56
|
+
return [segment, position];
|
|
57
|
+
}).sort(function byDisplayKey(a, b) {
|
|
58
|
+
return key(a[0]) - key(b[0]) || a[1] - b[1];
|
|
59
|
+
}).map(function unwrap(pair) {
|
|
60
|
+
return pair[0];
|
|
61
|
+
});
|
|
62
|
+
}
|
|
43
63
|
|
|
44
64
|
// src/core/specs.ts
|
|
45
65
|
var maxClockTimes = 6;
|
|
@@ -201,7 +221,17 @@ function renderSecondsWithinMinute(ir, plan, opts) {
|
|
|
201
221
|
}
|
|
202
222
|
function composeHourCadence(ir, plan, opts) {
|
|
203
223
|
const clockRest = plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes";
|
|
204
|
-
|
|
224
|
+
if (!clockRest || ir.shapes.minute !== "single") {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const minute = +ir.pattern.minute;
|
|
228
|
+
return hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
|
|
229
|
+
}
|
|
230
|
+
function clockTimesConfinement(ir, rest, opts) {
|
|
231
|
+
if (+rest.times[0].minute === 0 && ir.shapes.minute === "single") {
|
|
232
|
+
return secondsLeadClause(ir, opts) + " for one minute at " + durationHours(ir, rest, opts);
|
|
233
|
+
}
|
|
234
|
+
return secondsLeadClause(ir, opts) + " of " + clockTimesOf(ir, rest, opts);
|
|
205
235
|
}
|
|
206
236
|
function renderComposeSeconds(ir, plan, opts) {
|
|
207
237
|
const cadence = composeHourCadence(ir, plan, opts);
|
|
@@ -209,16 +239,14 @@ function renderComposeSeconds(ir, plan, opts) {
|
|
|
209
239
|
return cadence;
|
|
210
240
|
}
|
|
211
241
|
if (plan.rest.kind === "clockTimes" && (ir.shapes.second === "wildcard" || ir.shapes.second === "step")) {
|
|
212
|
-
|
|
213
|
-
if (+minute === 0) {
|
|
214
|
-
return secondsLeadClause(ir, opts) + " for one minute at " + durationHours(ir, plan.rest, opts);
|
|
215
|
-
}
|
|
216
|
-
return secondsLeadClause(ir, opts) + " of " + clockTimesOf(ir, plan.rest, opts);
|
|
242
|
+
return clockTimesConfinement(ir, plan.rest, opts);
|
|
217
243
|
}
|
|
218
244
|
if (ir.shapes.second === "wildcard" && plan.rest.kind === "minuteFrequency" && plan.rest.hours.kind === "none" && ir.pattern.minute === "*/2") {
|
|
219
245
|
return "every second of every other minute" + trailingQualifier(ir, opts);
|
|
220
246
|
}
|
|
221
|
-
|
|
247
|
+
const restOwnsLead = plan.rest.kind === "compactClockTimes" && ir.analyses.clockSecond;
|
|
248
|
+
const lead = restOwnsLead ? "" : secondsLeadClause(ir, opts) + ", ";
|
|
249
|
+
return lead + render(ir, plan.rest, opts);
|
|
222
250
|
}
|
|
223
251
|
function durationHours(ir, plan, opts) {
|
|
224
252
|
const hours = plan.times.map(function clock(time) {
|
|
@@ -301,7 +329,8 @@ function renderMinuteFrequency(ir, plan, opts) {
|
|
|
301
329
|
opts
|
|
302
330
|
);
|
|
303
331
|
if (plan.hours.kind === "during") {
|
|
304
|
-
|
|
332
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
333
|
+
phrase += cadence ? ", " + cadence : " during the " + hourTimesFromPlan(ir, plan.hours.times, false, opts) + " hours";
|
|
305
334
|
} else if (plan.hours.kind === "window") {
|
|
306
335
|
phrase += " " + hourWindow(plan.hours, opts);
|
|
307
336
|
} else if (plan.hours.kind === "step") {
|
|
@@ -316,10 +345,13 @@ function renderMinuteSpanInHour(ir, plan, opts) {
|
|
|
316
345
|
return "every minute from " + getTime({ hour: plan.hour, minute: plan.span[0] }, opts) + through(opts) + getTime({ hour: plan.hour, minute: plan.span[1] }, opts) + trailingQualifier(ir, opts);
|
|
317
346
|
}
|
|
318
347
|
function renderMinutesAcrossHours(ir, plan, opts) {
|
|
348
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
319
349
|
if (plan.form === "wildcard") {
|
|
350
|
+
if (cadence !== null) {
|
|
351
|
+
return "every minute, " + cadence + trailingQualifier(ir, opts);
|
|
352
|
+
}
|
|
320
353
|
return "every minute during the " + hourTimesFromPlan(ir, plan.times, false, opts) + " hours" + trailingQualifier(ir, opts);
|
|
321
354
|
}
|
|
322
|
-
const times = hourTimesFromPlan(ir, plan.times, true, opts);
|
|
323
355
|
const lead = plan.form === "range" ? minuteRangeLead(ir.pattern.minute, opts) : (
|
|
324
356
|
// The 'list' form is a minute list, which has segments; an offset/uneven
|
|
325
357
|
// step enumerated to that list reads as a stride.
|
|
@@ -330,6 +362,10 @@ function renderMinutesAcrossHours(ir, plan, opts) {
|
|
|
330
362
|
opts
|
|
331
363
|
)
|
|
332
364
|
);
|
|
365
|
+
if (cadence !== null) {
|
|
366
|
+
return lead + ", " + cadence + trailingQualifier(ir, opts);
|
|
367
|
+
}
|
|
368
|
+
const times = hourTimesFromPlan(ir, plan.times, true, opts);
|
|
333
369
|
return lead + ", at " + times + trailingQualifier(ir, opts);
|
|
334
370
|
}
|
|
335
371
|
var stepOrdinals = {
|
|
@@ -356,7 +392,8 @@ function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
|
|
|
356
392
|
"hour",
|
|
357
393
|
opts
|
|
358
394
|
) : minuteRangeLead(ir.pattern.minute, opts);
|
|
359
|
-
|
|
395
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
396
|
+
return lead + ", " + (cadence ?? stepHours(segment, opts)) + trailingQualifier(ir, opts);
|
|
360
397
|
}
|
|
361
398
|
function minuteRangeLead(minuteField, opts) {
|
|
362
399
|
const bounds = minuteField.split("-");
|
|
@@ -393,17 +430,23 @@ function rangeMinuteLead(ir, opts) {
|
|
|
393
430
|
);
|
|
394
431
|
}
|
|
395
432
|
function renderHourStep(ir, plan, opts) {
|
|
433
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
434
|
+
if (cadence !== null) {
|
|
435
|
+
return cadence + trailingQualifier(ir, opts);
|
|
436
|
+
}
|
|
396
437
|
return stepHours(ir.analyses.segments.hour[0], opts) + trailingQualifier(ir, opts);
|
|
397
438
|
}
|
|
398
439
|
function boundedWindow(plan) {
|
|
399
|
-
|
|
440
|
+
const last = plan.minuteForm === "wildcard" ? plan.boundMinute ?? 0 : 0;
|
|
441
|
+
return { from: plan.from, last, to: plan.to };
|
|
400
442
|
}
|
|
401
443
|
function hourWindow(window, opts) {
|
|
402
444
|
return "from " + getTime({ hour: window.from, minute: 0 }, opts) + through(opts) + getTime({ hour: window.to, minute: window.last }, opts);
|
|
403
445
|
}
|
|
404
446
|
function renderClockTimes(ir, plan, opts) {
|
|
405
447
|
if (ir.shapes.minute === "single") {
|
|
406
|
-
const
|
|
448
|
+
const minute = +ir.pattern.minute;
|
|
449
|
+
const cadence = hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
|
|
407
450
|
if (cadence !== null) {
|
|
408
451
|
return cadence;
|
|
409
452
|
}
|
|
@@ -421,9 +464,9 @@ function renderClockTimes(ir, plan, opts) {
|
|
|
421
464
|
}
|
|
422
465
|
function renderCompactClockTimes(ir, plan, opts) {
|
|
423
466
|
if (plan.fold) {
|
|
424
|
-
const
|
|
425
|
-
if (
|
|
426
|
-
return
|
|
467
|
+
const cadence2 = hourCadence(ir, +plan.minute, opts) ?? hourRangeCadence(ir, +plan.minute, opts);
|
|
468
|
+
if (cadence2 !== null) {
|
|
469
|
+
return cadence2;
|
|
427
470
|
}
|
|
428
471
|
const hasRange = ir.analyses.segments.hour.some(function range(segment) {
|
|
429
472
|
return segment.kind === "range";
|
|
@@ -434,16 +477,18 @@ function renderCompactClockTimes(ir, plan, opts) {
|
|
|
434
477
|
const fold = { minute: plan.minute, second: ir.analyses.clockSecond };
|
|
435
478
|
return interpretDayQualifier(ir, opts) + "at " + hourSegmentTimes(ir, fold, true, opts);
|
|
436
479
|
}
|
|
437
|
-
const
|
|
480
|
+
const minuteLead = (
|
|
438
481
|
// The non-fold branch is a minute list, which has segments. An
|
|
439
482
|
// offset/uneven step enumerated to that list reads as a stride.
|
|
440
|
-
|
|
483
|
+
strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
|
|
441
484
|
segmentWords(ir.analyses.segments.minute, opts),
|
|
442
485
|
"minute",
|
|
443
486
|
"hour",
|
|
444
487
|
opts
|
|
445
|
-
)
|
|
488
|
+
)
|
|
446
489
|
);
|
|
490
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
491
|
+
const phrase = cadence ? minuteLead + ", " + cadence + trailingQualifier(ir, opts) : minuteLead + ", at " + hourSegmentTimes(ir, { minute: 0, second: null }, true, opts) + trailingQualifier(ir, opts);
|
|
447
492
|
return ir.analyses.clockSecond ? secondsLeadClause(ir, opts) + ", " + phrase : phrase;
|
|
448
493
|
}
|
|
449
494
|
function foldedHourWindows(ir, plan, opts) {
|
|
@@ -561,16 +606,46 @@ function hourStrideCadence(stride, opts) {
|
|
|
561
606
|
}
|
|
562
607
|
return cadence + " from " + getTime({ hour: start, minute: 0 }, opts) + through(opts) + getTime({ hour: last, minute: 0 }, opts);
|
|
563
608
|
}
|
|
609
|
+
function offsetCleanStride(stride) {
|
|
610
|
+
return stride.start < stride.interval && 24 % stride.interval === 0;
|
|
611
|
+
}
|
|
612
|
+
function unevenHourCadence(ir, opts) {
|
|
613
|
+
const stride = hourStride(ir);
|
|
614
|
+
if (!stride || offsetCleanStride(stride)) {
|
|
615
|
+
return null;
|
|
616
|
+
}
|
|
617
|
+
return hourStrideCadence(stride, opts);
|
|
618
|
+
}
|
|
619
|
+
function hourListStride(values) {
|
|
620
|
+
if (values.length < 2) {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
const interval = values[1] - values[0];
|
|
624
|
+
if (interval < 2) {
|
|
625
|
+
return null;
|
|
626
|
+
}
|
|
627
|
+
for (let i = 2; i < values.length; i += 1) {
|
|
628
|
+
if (values[i] - values[i - 1] !== interval) {
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
if (values[0] !== 0 && values.length < 5) {
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
return { interval, last: values[values.length - 1], start: values[0] };
|
|
636
|
+
}
|
|
564
637
|
function hourStride(ir) {
|
|
565
638
|
const segments = ir.analyses.segments.hour;
|
|
566
639
|
if (segments.length === 1 && segments[0].kind === "step") {
|
|
567
640
|
const segment = segments[0];
|
|
641
|
+
if (segment.fires.length < 2) {
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
568
644
|
const start = segment.startToken === "*" ? 0 : +segment.startToken.split("-")[0];
|
|
569
645
|
return { interval: segment.interval, last: segment.fires[segment.fires.length - 1], start };
|
|
570
646
|
}
|
|
571
647
|
const values = singleValues(segments);
|
|
572
|
-
|
|
573
|
-
return step || null;
|
|
648
|
+
return values && hourListStride(values);
|
|
574
649
|
}
|
|
575
650
|
function subMinuteSecond(ir) {
|
|
576
651
|
return ir.pattern.second === "*" || ir.shapes.second === "step";
|
|
@@ -594,13 +669,16 @@ function hourCadence(ir, minute, opts) {
|
|
|
594
669
|
return null;
|
|
595
670
|
}
|
|
596
671
|
const fires = (stride.last - stride.start) / stride.interval + 1;
|
|
597
|
-
if (ir.pattern.second === "0" && fires <= maxClockTimes) {
|
|
672
|
+
if (ir.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
|
|
598
673
|
return null;
|
|
599
674
|
}
|
|
600
675
|
const confinement = minute === 0 && subMinuteSecond(ir) && cleanStrideSegment(ir);
|
|
601
676
|
if (confinement) {
|
|
602
677
|
return secondsClause(ir, "minute", opts) + " for one minute " + everyNthHour(confinement, opts) + trailingQualifier(ir, opts);
|
|
603
678
|
}
|
|
679
|
+
if (minute === 0 && ir.pattern.second === "0") {
|
|
680
|
+
return hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
|
|
681
|
+
}
|
|
604
682
|
return hourCadenceLead(ir, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
|
|
605
683
|
}
|
|
606
684
|
function cleanStrideSegment(ir) {
|
|
@@ -611,6 +689,46 @@ function cleanStrideSegment(ir) {
|
|
|
611
689
|
}
|
|
612
690
|
return segment;
|
|
613
691
|
}
|
|
692
|
+
function hasHourWindow(ir) {
|
|
693
|
+
return ir.analyses.segments.hour.some(function range(segment) {
|
|
694
|
+
return segment.kind === "range";
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
function hourRangeWindowTail(ir, opts) {
|
|
698
|
+
const windows = [];
|
|
699
|
+
const singles = [];
|
|
700
|
+
ir.analyses.segments.hour.forEach(function classify(segment) {
|
|
701
|
+
if (segment.kind === "range") {
|
|
702
|
+
windows.push("from " + getTime(
|
|
703
|
+
{ hour: +segment.bounds[0], minute: 0 },
|
|
704
|
+
opts
|
|
705
|
+
) + through(opts) + getTime({ hour: +segment.bounds[1], minute: 0 }, opts));
|
|
706
|
+
} else if (segment.kind === "step") {
|
|
707
|
+
singles.push(...segment.fires);
|
|
708
|
+
} else {
|
|
709
|
+
singles.push(+segment.value);
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
let phrase = "every hour " + joinList(windows, opts);
|
|
713
|
+
if (singles.length) {
|
|
714
|
+
phrase += " and at " + joinList(singles.map(function time(hour) {
|
|
715
|
+
return getTime({ hour, minute: 0 }, opts);
|
|
716
|
+
}), opts);
|
|
717
|
+
}
|
|
718
|
+
return phrase;
|
|
719
|
+
}
|
|
720
|
+
function hourRangeCadence(ir, minute, opts) {
|
|
721
|
+
if (minute !== 0 || !hasHourWindow(ir)) {
|
|
722
|
+
return null;
|
|
723
|
+
}
|
|
724
|
+
if (ir.pattern.second === "0") {
|
|
725
|
+
return null;
|
|
726
|
+
}
|
|
727
|
+
if (subMinuteSecond(ir)) {
|
|
728
|
+
return secondsClause(ir, "minute", opts) + " for one minute during the " + hourSegmentTimes(ir, { minute: 0, second: null }, false, opts) + " hours" + trailingQualifier(ir, opts);
|
|
729
|
+
}
|
|
730
|
+
return hourCadenceLead(ir, minute, opts) + ", " + hourRangeWindowTail(ir, opts) + trailingQualifier(ir, opts);
|
|
731
|
+
}
|
|
614
732
|
function seriesNumber(values, opts) {
|
|
615
733
|
const anyBig = values.some(function big(v) {
|
|
616
734
|
return +v > 10;
|
|
@@ -775,17 +893,27 @@ function monthFoldsIntoDate(ir) {
|
|
|
775
893
|
function dateOrWeekday(ir, opts) {
|
|
776
894
|
const pattern = ir.pattern;
|
|
777
895
|
const weekdayPart = quartzWeekdayPhrase(pattern.weekday, opts) || "on " + weekdayPhrase(ir, opts);
|
|
896
|
+
if (pattern.month !== "*" && monthFoldsIntoDate(ir) && !quartzDatePhrase(pattern.date, opts) && !isOpenStep(pattern.date)) {
|
|
897
|
+
return "on " + monthDatePhrase(ir, opts) + " or " + weekdayPart + " in " + monthName(ir, opts);
|
|
898
|
+
}
|
|
899
|
+
return datePart(ir, opts) + " or " + weekdayPart + orMonthScope(ir, opts);
|
|
900
|
+
}
|
|
901
|
+
function datePart(ir, opts) {
|
|
902
|
+
const pattern = ir.pattern;
|
|
778
903
|
const quartzDate = quartzDatePhrase(pattern.date, opts);
|
|
779
904
|
if (quartzDate) {
|
|
780
|
-
return quartzDate
|
|
905
|
+
return quartzDate;
|
|
781
906
|
}
|
|
782
907
|
if (isOpenStep(pattern.date)) {
|
|
783
|
-
return stepDates(pattern.date)
|
|
908
|
+
return stepDates(pattern.date);
|
|
784
909
|
}
|
|
785
|
-
|
|
786
|
-
|
|
910
|
+
return "on the " + dateOrdinals(ir, opts);
|
|
911
|
+
}
|
|
912
|
+
function orMonthScope(ir, opts) {
|
|
913
|
+
if (ir.pattern.month === "*") {
|
|
914
|
+
return "";
|
|
787
915
|
}
|
|
788
|
-
return "
|
|
916
|
+
return ", in " + monthName(ir, opts);
|
|
789
917
|
}
|
|
790
918
|
function quartzDatePhrase(dateField, opts) {
|
|
791
919
|
if (dateField === "L") {
|
|
@@ -867,7 +995,8 @@ function oddEvenMonth(monthField) {
|
|
|
867
995
|
return start === "2" ? "every even-numbered month" : null;
|
|
868
996
|
}
|
|
869
997
|
function weekdayPhrase(ir, opts) {
|
|
870
|
-
|
|
998
|
+
const segments = orderWeekdaysForDisplay(ir.analyses.segments.weekday);
|
|
999
|
+
return renderSegments(segments, function name(value) {
|
|
871
1000
|
return getWeekday(value, opts);
|
|
872
1001
|
}, opts);
|
|
873
1002
|
}
|
package/dist/lang/en.js
CHANGED
|
@@ -14,6 +14,26 @@ function arithmeticStep(values) {
|
|
|
14
14
|
}
|
|
15
15
|
return { start: values[0], interval, last: values[values.length - 1] };
|
|
16
16
|
}
|
|
17
|
+
function weekdayDisplayKey(value) {
|
|
18
|
+
return value === 0 ? 7 : value;
|
|
19
|
+
}
|
|
20
|
+
function orderWeekdaysForDisplay(segments) {
|
|
21
|
+
const flattened = segments.flatMap(function flat(segment) {
|
|
22
|
+
return segment.kind === "step" ? segment.fires.map(function single(value) {
|
|
23
|
+
return { kind: "single", value: "" + value };
|
|
24
|
+
}) : [segment];
|
|
25
|
+
});
|
|
26
|
+
function key(segment) {
|
|
27
|
+
return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
|
|
28
|
+
}
|
|
29
|
+
return flattened.map(function index(segment, position) {
|
|
30
|
+
return [segment, position];
|
|
31
|
+
}).sort(function byDisplayKey(a, b) {
|
|
32
|
+
return key(a[0]) - key(b[0]) || a[1] - b[1];
|
|
33
|
+
}).map(function unwrap(pair) {
|
|
34
|
+
return pair[0];
|
|
35
|
+
});
|
|
36
|
+
}
|
|
17
37
|
|
|
18
38
|
// src/core/specs.ts
|
|
19
39
|
var maxClockTimes = 6;
|
|
@@ -175,7 +195,17 @@ function renderSecondsWithinMinute(ir, plan, opts) {
|
|
|
175
195
|
}
|
|
176
196
|
function composeHourCadence(ir, plan, opts) {
|
|
177
197
|
const clockRest = plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes";
|
|
178
|
-
|
|
198
|
+
if (!clockRest || ir.shapes.minute !== "single") {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
const minute = +ir.pattern.minute;
|
|
202
|
+
return hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
|
|
203
|
+
}
|
|
204
|
+
function clockTimesConfinement(ir, rest, opts) {
|
|
205
|
+
if (+rest.times[0].minute === 0 && ir.shapes.minute === "single") {
|
|
206
|
+
return secondsLeadClause(ir, opts) + " for one minute at " + durationHours(ir, rest, opts);
|
|
207
|
+
}
|
|
208
|
+
return secondsLeadClause(ir, opts) + " of " + clockTimesOf(ir, rest, opts);
|
|
179
209
|
}
|
|
180
210
|
function renderComposeSeconds(ir, plan, opts) {
|
|
181
211
|
const cadence = composeHourCadence(ir, plan, opts);
|
|
@@ -183,16 +213,14 @@ function renderComposeSeconds(ir, plan, opts) {
|
|
|
183
213
|
return cadence;
|
|
184
214
|
}
|
|
185
215
|
if (plan.rest.kind === "clockTimes" && (ir.shapes.second === "wildcard" || ir.shapes.second === "step")) {
|
|
186
|
-
|
|
187
|
-
if (+minute === 0) {
|
|
188
|
-
return secondsLeadClause(ir, opts) + " for one minute at " + durationHours(ir, plan.rest, opts);
|
|
189
|
-
}
|
|
190
|
-
return secondsLeadClause(ir, opts) + " of " + clockTimesOf(ir, plan.rest, opts);
|
|
216
|
+
return clockTimesConfinement(ir, plan.rest, opts);
|
|
191
217
|
}
|
|
192
218
|
if (ir.shapes.second === "wildcard" && plan.rest.kind === "minuteFrequency" && plan.rest.hours.kind === "none" && ir.pattern.minute === "*/2") {
|
|
193
219
|
return "every second of every other minute" + trailingQualifier(ir, opts);
|
|
194
220
|
}
|
|
195
|
-
|
|
221
|
+
const restOwnsLead = plan.rest.kind === "compactClockTimes" && ir.analyses.clockSecond;
|
|
222
|
+
const lead = restOwnsLead ? "" : secondsLeadClause(ir, opts) + ", ";
|
|
223
|
+
return lead + render(ir, plan.rest, opts);
|
|
196
224
|
}
|
|
197
225
|
function durationHours(ir, plan, opts) {
|
|
198
226
|
const hours = plan.times.map(function clock(time) {
|
|
@@ -275,7 +303,8 @@ function renderMinuteFrequency(ir, plan, opts) {
|
|
|
275
303
|
opts
|
|
276
304
|
);
|
|
277
305
|
if (plan.hours.kind === "during") {
|
|
278
|
-
|
|
306
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
307
|
+
phrase += cadence ? ", " + cadence : " during the " + hourTimesFromPlan(ir, plan.hours.times, false, opts) + " hours";
|
|
279
308
|
} else if (plan.hours.kind === "window") {
|
|
280
309
|
phrase += " " + hourWindow(plan.hours, opts);
|
|
281
310
|
} else if (plan.hours.kind === "step") {
|
|
@@ -290,10 +319,13 @@ function renderMinuteSpanInHour(ir, plan, opts) {
|
|
|
290
319
|
return "every minute from " + getTime({ hour: plan.hour, minute: plan.span[0] }, opts) + through(opts) + getTime({ hour: plan.hour, minute: plan.span[1] }, opts) + trailingQualifier(ir, opts);
|
|
291
320
|
}
|
|
292
321
|
function renderMinutesAcrossHours(ir, plan, opts) {
|
|
322
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
293
323
|
if (plan.form === "wildcard") {
|
|
324
|
+
if (cadence !== null) {
|
|
325
|
+
return "every minute, " + cadence + trailingQualifier(ir, opts);
|
|
326
|
+
}
|
|
294
327
|
return "every minute during the " + hourTimesFromPlan(ir, plan.times, false, opts) + " hours" + trailingQualifier(ir, opts);
|
|
295
328
|
}
|
|
296
|
-
const times = hourTimesFromPlan(ir, plan.times, true, opts);
|
|
297
329
|
const lead = plan.form === "range" ? minuteRangeLead(ir.pattern.minute, opts) : (
|
|
298
330
|
// The 'list' form is a minute list, which has segments; an offset/uneven
|
|
299
331
|
// step enumerated to that list reads as a stride.
|
|
@@ -304,6 +336,10 @@ function renderMinutesAcrossHours(ir, plan, opts) {
|
|
|
304
336
|
opts
|
|
305
337
|
)
|
|
306
338
|
);
|
|
339
|
+
if (cadence !== null) {
|
|
340
|
+
return lead + ", " + cadence + trailingQualifier(ir, opts);
|
|
341
|
+
}
|
|
342
|
+
const times = hourTimesFromPlan(ir, plan.times, true, opts);
|
|
307
343
|
return lead + ", at " + times + trailingQualifier(ir, opts);
|
|
308
344
|
}
|
|
309
345
|
var stepOrdinals = {
|
|
@@ -330,7 +366,8 @@ function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
|
|
|
330
366
|
"hour",
|
|
331
367
|
opts
|
|
332
368
|
) : minuteRangeLead(ir.pattern.minute, opts);
|
|
333
|
-
|
|
369
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
370
|
+
return lead + ", " + (cadence ?? stepHours(segment, opts)) + trailingQualifier(ir, opts);
|
|
334
371
|
}
|
|
335
372
|
function minuteRangeLead(minuteField, opts) {
|
|
336
373
|
const bounds = minuteField.split("-");
|
|
@@ -367,17 +404,23 @@ function rangeMinuteLead(ir, opts) {
|
|
|
367
404
|
);
|
|
368
405
|
}
|
|
369
406
|
function renderHourStep(ir, plan, opts) {
|
|
407
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
408
|
+
if (cadence !== null) {
|
|
409
|
+
return cadence + trailingQualifier(ir, opts);
|
|
410
|
+
}
|
|
370
411
|
return stepHours(ir.analyses.segments.hour[0], opts) + trailingQualifier(ir, opts);
|
|
371
412
|
}
|
|
372
413
|
function boundedWindow(plan) {
|
|
373
|
-
|
|
414
|
+
const last = plan.minuteForm === "wildcard" ? plan.boundMinute ?? 0 : 0;
|
|
415
|
+
return { from: plan.from, last, to: plan.to };
|
|
374
416
|
}
|
|
375
417
|
function hourWindow(window, opts) {
|
|
376
418
|
return "from " + getTime({ hour: window.from, minute: 0 }, opts) + through(opts) + getTime({ hour: window.to, minute: window.last }, opts);
|
|
377
419
|
}
|
|
378
420
|
function renderClockTimes(ir, plan, opts) {
|
|
379
421
|
if (ir.shapes.minute === "single") {
|
|
380
|
-
const
|
|
422
|
+
const minute = +ir.pattern.minute;
|
|
423
|
+
const cadence = hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
|
|
381
424
|
if (cadence !== null) {
|
|
382
425
|
return cadence;
|
|
383
426
|
}
|
|
@@ -395,9 +438,9 @@ function renderClockTimes(ir, plan, opts) {
|
|
|
395
438
|
}
|
|
396
439
|
function renderCompactClockTimes(ir, plan, opts) {
|
|
397
440
|
if (plan.fold) {
|
|
398
|
-
const
|
|
399
|
-
if (
|
|
400
|
-
return
|
|
441
|
+
const cadence2 = hourCadence(ir, +plan.minute, opts) ?? hourRangeCadence(ir, +plan.minute, opts);
|
|
442
|
+
if (cadence2 !== null) {
|
|
443
|
+
return cadence2;
|
|
401
444
|
}
|
|
402
445
|
const hasRange = ir.analyses.segments.hour.some(function range(segment) {
|
|
403
446
|
return segment.kind === "range";
|
|
@@ -408,16 +451,18 @@ function renderCompactClockTimes(ir, plan, opts) {
|
|
|
408
451
|
const fold = { minute: plan.minute, second: ir.analyses.clockSecond };
|
|
409
452
|
return interpretDayQualifier(ir, opts) + "at " + hourSegmentTimes(ir, fold, true, opts);
|
|
410
453
|
}
|
|
411
|
-
const
|
|
454
|
+
const minuteLead = (
|
|
412
455
|
// The non-fold branch is a minute list, which has segments. An
|
|
413
456
|
// offset/uneven step enumerated to that list reads as a stride.
|
|
414
|
-
|
|
457
|
+
strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
|
|
415
458
|
segmentWords(ir.analyses.segments.minute, opts),
|
|
416
459
|
"minute",
|
|
417
460
|
"hour",
|
|
418
461
|
opts
|
|
419
|
-
)
|
|
462
|
+
)
|
|
420
463
|
);
|
|
464
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
465
|
+
const phrase = cadence ? minuteLead + ", " + cadence + trailingQualifier(ir, opts) : minuteLead + ", at " + hourSegmentTimes(ir, { minute: 0, second: null }, true, opts) + trailingQualifier(ir, opts);
|
|
421
466
|
return ir.analyses.clockSecond ? secondsLeadClause(ir, opts) + ", " + phrase : phrase;
|
|
422
467
|
}
|
|
423
468
|
function foldedHourWindows(ir, plan, opts) {
|
|
@@ -535,16 +580,46 @@ function hourStrideCadence(stride, opts) {
|
|
|
535
580
|
}
|
|
536
581
|
return cadence + " from " + getTime({ hour: start, minute: 0 }, opts) + through(opts) + getTime({ hour: last, minute: 0 }, opts);
|
|
537
582
|
}
|
|
583
|
+
function offsetCleanStride(stride) {
|
|
584
|
+
return stride.start < stride.interval && 24 % stride.interval === 0;
|
|
585
|
+
}
|
|
586
|
+
function unevenHourCadence(ir, opts) {
|
|
587
|
+
const stride = hourStride(ir);
|
|
588
|
+
if (!stride || offsetCleanStride(stride)) {
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
return hourStrideCadence(stride, opts);
|
|
592
|
+
}
|
|
593
|
+
function hourListStride(values) {
|
|
594
|
+
if (values.length < 2) {
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
const interval = values[1] - values[0];
|
|
598
|
+
if (interval < 2) {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
for (let i = 2; i < values.length; i += 1) {
|
|
602
|
+
if (values[i] - values[i - 1] !== interval) {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (values[0] !== 0 && values.length < 5) {
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
return { interval, last: values[values.length - 1], start: values[0] };
|
|
610
|
+
}
|
|
538
611
|
function hourStride(ir) {
|
|
539
612
|
const segments = ir.analyses.segments.hour;
|
|
540
613
|
if (segments.length === 1 && segments[0].kind === "step") {
|
|
541
614
|
const segment = segments[0];
|
|
615
|
+
if (segment.fires.length < 2) {
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
542
618
|
const start = segment.startToken === "*" ? 0 : +segment.startToken.split("-")[0];
|
|
543
619
|
return { interval: segment.interval, last: segment.fires[segment.fires.length - 1], start };
|
|
544
620
|
}
|
|
545
621
|
const values = singleValues(segments);
|
|
546
|
-
|
|
547
|
-
return step || null;
|
|
622
|
+
return values && hourListStride(values);
|
|
548
623
|
}
|
|
549
624
|
function subMinuteSecond(ir) {
|
|
550
625
|
return ir.pattern.second === "*" || ir.shapes.second === "step";
|
|
@@ -568,13 +643,16 @@ function hourCadence(ir, minute, opts) {
|
|
|
568
643
|
return null;
|
|
569
644
|
}
|
|
570
645
|
const fires = (stride.last - stride.start) / stride.interval + 1;
|
|
571
|
-
if (ir.pattern.second === "0" && fires <= maxClockTimes) {
|
|
646
|
+
if (ir.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
|
|
572
647
|
return null;
|
|
573
648
|
}
|
|
574
649
|
const confinement = minute === 0 && subMinuteSecond(ir) && cleanStrideSegment(ir);
|
|
575
650
|
if (confinement) {
|
|
576
651
|
return secondsClause(ir, "minute", opts) + " for one minute " + everyNthHour(confinement, opts) + trailingQualifier(ir, opts);
|
|
577
652
|
}
|
|
653
|
+
if (minute === 0 && ir.pattern.second === "0") {
|
|
654
|
+
return hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
|
|
655
|
+
}
|
|
578
656
|
return hourCadenceLead(ir, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
|
|
579
657
|
}
|
|
580
658
|
function cleanStrideSegment(ir) {
|
|
@@ -585,6 +663,46 @@ function cleanStrideSegment(ir) {
|
|
|
585
663
|
}
|
|
586
664
|
return segment;
|
|
587
665
|
}
|
|
666
|
+
function hasHourWindow(ir) {
|
|
667
|
+
return ir.analyses.segments.hour.some(function range(segment) {
|
|
668
|
+
return segment.kind === "range";
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
function hourRangeWindowTail(ir, opts) {
|
|
672
|
+
const windows = [];
|
|
673
|
+
const singles = [];
|
|
674
|
+
ir.analyses.segments.hour.forEach(function classify(segment) {
|
|
675
|
+
if (segment.kind === "range") {
|
|
676
|
+
windows.push("from " + getTime(
|
|
677
|
+
{ hour: +segment.bounds[0], minute: 0 },
|
|
678
|
+
opts
|
|
679
|
+
) + through(opts) + getTime({ hour: +segment.bounds[1], minute: 0 }, opts));
|
|
680
|
+
} else if (segment.kind === "step") {
|
|
681
|
+
singles.push(...segment.fires);
|
|
682
|
+
} else {
|
|
683
|
+
singles.push(+segment.value);
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
let phrase = "every hour " + joinList(windows, opts);
|
|
687
|
+
if (singles.length) {
|
|
688
|
+
phrase += " and at " + joinList(singles.map(function time(hour) {
|
|
689
|
+
return getTime({ hour, minute: 0 }, opts);
|
|
690
|
+
}), opts);
|
|
691
|
+
}
|
|
692
|
+
return phrase;
|
|
693
|
+
}
|
|
694
|
+
function hourRangeCadence(ir, minute, opts) {
|
|
695
|
+
if (minute !== 0 || !hasHourWindow(ir)) {
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
if (ir.pattern.second === "0") {
|
|
699
|
+
return null;
|
|
700
|
+
}
|
|
701
|
+
if (subMinuteSecond(ir)) {
|
|
702
|
+
return secondsClause(ir, "minute", opts) + " for one minute during the " + hourSegmentTimes(ir, { minute: 0, second: null }, false, opts) + " hours" + trailingQualifier(ir, opts);
|
|
703
|
+
}
|
|
704
|
+
return hourCadenceLead(ir, minute, opts) + ", " + hourRangeWindowTail(ir, opts) + trailingQualifier(ir, opts);
|
|
705
|
+
}
|
|
588
706
|
function seriesNumber(values, opts) {
|
|
589
707
|
const anyBig = values.some(function big(v) {
|
|
590
708
|
return +v > 10;
|
|
@@ -749,17 +867,27 @@ function monthFoldsIntoDate(ir) {
|
|
|
749
867
|
function dateOrWeekday(ir, opts) {
|
|
750
868
|
const pattern = ir.pattern;
|
|
751
869
|
const weekdayPart = quartzWeekdayPhrase(pattern.weekday, opts) || "on " + weekdayPhrase(ir, opts);
|
|
870
|
+
if (pattern.month !== "*" && monthFoldsIntoDate(ir) && !quartzDatePhrase(pattern.date, opts) && !isOpenStep(pattern.date)) {
|
|
871
|
+
return "on " + monthDatePhrase(ir, opts) + " or " + weekdayPart + " in " + monthName(ir, opts);
|
|
872
|
+
}
|
|
873
|
+
return datePart(ir, opts) + " or " + weekdayPart + orMonthScope(ir, opts);
|
|
874
|
+
}
|
|
875
|
+
function datePart(ir, opts) {
|
|
876
|
+
const pattern = ir.pattern;
|
|
752
877
|
const quartzDate = quartzDatePhrase(pattern.date, opts);
|
|
753
878
|
if (quartzDate) {
|
|
754
|
-
return quartzDate
|
|
879
|
+
return quartzDate;
|
|
755
880
|
}
|
|
756
881
|
if (isOpenStep(pattern.date)) {
|
|
757
|
-
return stepDates(pattern.date)
|
|
882
|
+
return stepDates(pattern.date);
|
|
758
883
|
}
|
|
759
|
-
|
|
760
|
-
|
|
884
|
+
return "on the " + dateOrdinals(ir, opts);
|
|
885
|
+
}
|
|
886
|
+
function orMonthScope(ir, opts) {
|
|
887
|
+
if (ir.pattern.month === "*") {
|
|
888
|
+
return "";
|
|
761
889
|
}
|
|
762
|
-
return "
|
|
890
|
+
return ", in " + monthName(ir, opts);
|
|
763
891
|
}
|
|
764
892
|
function quartzDatePhrase(dateField, opts) {
|
|
765
893
|
if (dateField === "L") {
|
|
@@ -841,7 +969,8 @@ function oddEvenMonth(monthField) {
|
|
|
841
969
|
return start === "2" ? "every even-numbered month" : null;
|
|
842
970
|
}
|
|
843
971
|
function weekdayPhrase(ir, opts) {
|
|
844
|
-
|
|
972
|
+
const segments = orderWeekdaysForDisplay(ir.analyses.segments.weekday);
|
|
973
|
+
return renderSegments(segments, function name(value) {
|
|
845
974
|
return getWeekday(value, opts);
|
|
846
975
|
}, opts);
|
|
847
976
|
}
|