cronli5 0.8.0 → 0.8.3
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 +49 -0
- package/README.md +6 -6
- package/cronli5.min.js +2 -2
- package/dist/cronli5.cjs +58 -8
- package/dist/cronli5.js +58 -8
- package/dist/lang/de.cjs +48 -9
- package/dist/lang/de.js +48 -9
- package/dist/lang/en.cjs +58 -8
- package/dist/lang/en.js +58 -8
- package/dist/lang/es.cjs +45 -3
- package/dist/lang/es.js +45 -3
- package/dist/lang/fi.cjs +50 -6
- package/dist/lang/fi.js +50 -6
- package/dist/lang/fr.cjs +45 -3
- package/dist/lang/fr.js +45 -3
- package/dist/lang/pt.cjs +45 -3
- package/dist/lang/pt.js +45 -3
- package/dist/lang/zh.cjs +52 -10
- package/dist/lang/zh.js +52 -10
- package/package.json +1 -1
- package/src/lang/de/index.ts +117 -30
- package/src/lang/en/index.ts +121 -28
- package/src/lang/es/index.ts +106 -7
- package/src/lang/fi/index.ts +121 -18
- package/src/lang/fr/index.ts +101 -7
- package/src/lang/pt/index.ts +101 -6
- package/src/lang/zh/index.ts +149 -16
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,16 +603,27 @@ 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)) {
|
|
605
613
|
return "\u6BCF\u5929" + restText + secTail;
|
|
606
614
|
}
|
|
607
615
|
if (rest.kind === "singleMinute") {
|
|
608
|
-
return restText + "\uFF0C" + sec;
|
|
616
|
+
return secondIsCadence(schedule) ? restText + confinedSecondTail(sec) : restText + "\uFF0C" + sec;
|
|
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";
|
|
@@ -663,17 +683,39 @@ function composeSecondsListed(schedule) {
|
|
|
663
683
|
return hourWord(hourFires(schedule)[0]) + minuteCad + "\u7684\u6BCF\u4E00\u79D2";
|
|
664
684
|
}
|
|
665
685
|
if (schedule.shapes.hour === "wildcard") {
|
|
666
|
-
return minutes + "\uFF0C" + sec;
|
|
686
|
+
return secondIsCadence(schedule) ? minutes + confinedSecondTail(sec) : minutes + "\uFF0C" + sec;
|
|
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
|
}
|
|
694
|
+
function isMinuteStride(schedule) {
|
|
695
|
+
if (schedule.shapes.minute === "step") {
|
|
696
|
+
return true;
|
|
697
|
+
}
|
|
698
|
+
const values = singleValues(segmentsOf(schedule, "minute"));
|
|
699
|
+
return values !== null && arithmeticStep(values) !== null;
|
|
700
|
+
}
|
|
701
|
+
function secondIsCadence(schedule) {
|
|
702
|
+
return schedule.pattern.second === "*" || schedule.shapes.second === "step";
|
|
703
|
+
}
|
|
704
|
+
function confinedSecondTail(sec) {
|
|
705
|
+
return sec === "\u6BCF\u79D2" ? "\u7684\u6BCF\u4E00\u79D2" : "\u7684" + sec;
|
|
706
|
+
}
|
|
707
|
+
function isSteppedMinuteSeconds(schedule, composedClock) {
|
|
708
|
+
return !composedClock && schedule.shapes.hour === "wildcard" && secondIsCadence(schedule) && schedule.pattern.minute !== "*/2" && isMinuteStride(schedule);
|
|
709
|
+
}
|
|
710
|
+
function minuteStrideConfinement(schedule) {
|
|
711
|
+
return minuteHourClause(schedule) + confinedSecondTail(secondClause(schedule));
|
|
712
|
+
}
|
|
674
713
|
function renderComposeSeconds(schedule, plan, opts) {
|
|
675
714
|
const { rest } = plan;
|
|
676
715
|
const composedClock = rest.kind === "clockTimes" || rest.kind === "compactClockTimes";
|
|
716
|
+
if (isSteppedMinuteSeconds(schedule, composedClock)) {
|
|
717
|
+
return minuteStrideConfinement(schedule);
|
|
718
|
+
}
|
|
677
719
|
if (schedule.pattern.minute === "0" || composedClock && schedule.shapes.minute === "single") {
|
|
678
720
|
return composeSecondsOnHour(schedule, plan, opts);
|
|
679
721
|
}
|
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,16 +577,27 @@ 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)) {
|
|
579
587
|
return "\u6BCF\u5929" + restText + secTail;
|
|
580
588
|
}
|
|
581
589
|
if (rest.kind === "singleMinute") {
|
|
582
|
-
return restText + "\uFF0C" + sec;
|
|
590
|
+
return secondIsCadence(schedule) ? restText + confinedSecondTail(sec) : restText + "\uFF0C" + sec;
|
|
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";
|
|
@@ -637,17 +657,39 @@ function composeSecondsListed(schedule) {
|
|
|
637
657
|
return hourWord(hourFires(schedule)[0]) + minuteCad + "\u7684\u6BCF\u4E00\u79D2";
|
|
638
658
|
}
|
|
639
659
|
if (schedule.shapes.hour === "wildcard") {
|
|
640
|
-
return minutes + "\uFF0C" + sec;
|
|
660
|
+
return secondIsCadence(schedule) ? minutes + confinedSecondTail(sec) : minutes + "\uFF0C" + sec;
|
|
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
|
}
|
|
668
|
+
function isMinuteStride(schedule) {
|
|
669
|
+
if (schedule.shapes.minute === "step") {
|
|
670
|
+
return true;
|
|
671
|
+
}
|
|
672
|
+
const values = singleValues(segmentsOf(schedule, "minute"));
|
|
673
|
+
return values !== null && arithmeticStep(values) !== null;
|
|
674
|
+
}
|
|
675
|
+
function secondIsCadence(schedule) {
|
|
676
|
+
return schedule.pattern.second === "*" || schedule.shapes.second === "step";
|
|
677
|
+
}
|
|
678
|
+
function confinedSecondTail(sec) {
|
|
679
|
+
return sec === "\u6BCF\u79D2" ? "\u7684\u6BCF\u4E00\u79D2" : "\u7684" + sec;
|
|
680
|
+
}
|
|
681
|
+
function isSteppedMinuteSeconds(schedule, composedClock) {
|
|
682
|
+
return !composedClock && schedule.shapes.hour === "wildcard" && secondIsCadence(schedule) && schedule.pattern.minute !== "*/2" && isMinuteStride(schedule);
|
|
683
|
+
}
|
|
684
|
+
function minuteStrideConfinement(schedule) {
|
|
685
|
+
return minuteHourClause(schedule) + confinedSecondTail(secondClause(schedule));
|
|
686
|
+
}
|
|
648
687
|
function renderComposeSeconds(schedule, plan, opts) {
|
|
649
688
|
const { rest } = plan;
|
|
650
689
|
const composedClock = rest.kind === "clockTimes" || rest.kind === "compactClockTimes";
|
|
690
|
+
if (isSteppedMinuteSeconds(schedule, composedClock)) {
|
|
691
|
+
return minuteStrideConfinement(schedule);
|
|
692
|
+
}
|
|
651
693
|
if (schedule.pattern.minute === "0" || composedClock && schedule.shapes.minute === "single") {
|
|
652
694
|
return composeSecondsOnHour(schedule, plan, opts);
|
|
653
695
|
}
|
package/package.json
CHANGED
package/src/lang/de/index.ts
CHANGED
|
@@ -223,6 +223,16 @@ const stepOrdinals: {[interval: number]: string} = {
|
|
|
223
223
|
12: 'zwölften'
|
|
224
224
|
};
|
|
225
225
|
|
|
226
|
+
// Dative ordinals for "in jeder N-ten Minute" — the step intervals a minute
|
|
227
|
+
// cadence can take. The interval-2 step keeps its own "jeder zweiten Minute"
|
|
228
|
+
// idiom and never reaches the confinement helper; a lookup miss falls back to
|
|
229
|
+
// the cardinal "alle N Minuten" form, which still confines.
|
|
230
|
+
const minuteStepOrdinals: {[interval: number]: string} = {
|
|
231
|
+
3: 'dritten', 4: 'vierten', 5: 'fünften', 6: 'sechsten', 7: 'siebten',
|
|
232
|
+
8: 'achten', 9: 'neunten', 10: 'zehnten', 12: 'zwölften',
|
|
233
|
+
15: 'fünfzehnten', 20: 'zwanzigsten', 30: 'dreißigsten'
|
|
234
|
+
};
|
|
235
|
+
|
|
226
236
|
// Confine a cadence to a clean hour stride: "in jeder zweiten Stunde", with
|
|
227
237
|
// the start named when it is not midnight ("…ab 1 Uhr" for an odd stride).
|
|
228
238
|
function everyNthHour(segment: StepSegment): string {
|
|
@@ -717,6 +727,65 @@ function isEveryOtherMinuteSeconds(
|
|
|
717
727
|
return minuteStep.startToken === '*' && minuteStep.interval === 2;
|
|
718
728
|
}
|
|
719
729
|
|
|
730
|
+
// The minute field's step stride for the confinement frame, or null when the
|
|
731
|
+
// minute is not a stepped cadence. A `step`-shaped field reads its segment; a
|
|
732
|
+
// `list`-shaped field the core enumerated from a uneven step (`2/7` → 2,9,…,58)
|
|
733
|
+
// recovers the progression from its values.
|
|
734
|
+
function minuteStride(
|
|
735
|
+
schedule: Schedule
|
|
736
|
+
): {start: number; interval: number; last: number} | null {
|
|
737
|
+
if (schedule.shapes.minute === 'step') {
|
|
738
|
+
const segment = stepSegment(schedule, 'minute');
|
|
739
|
+
const start = segment.startToken === '*' ? 0 : +segment.startToken;
|
|
740
|
+
|
|
741
|
+
return {interval: segment.interval, last:
|
|
742
|
+
segment.fires[segment.fires.length - 1], start};
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const values = singleValues(segmentsOf(schedule, 'minute'));
|
|
746
|
+
|
|
747
|
+
return values && arithmeticStep(values);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// A stepped minute under a wildcard/stepped second and wildcard hour: bind the
|
|
751
|
+
// second cadence to the minute cadence as a CONFINEMENT ("jede Sekunde in jeder
|
|
752
|
+
// sechsten Minute ab Minute 4 jeder Stunde"), never the comma juxtaposition
|
|
753
|
+
// that reads as two independent cadences. The cadence is ORDINAL ("in jeder
|
|
754
|
+
// sechsten Minute") — the cardinal "alle 6 Minuten" is what fuels the misread —
|
|
755
|
+
// and the start/bound mirror the standalone minute cadence.
|
|
756
|
+
function minuteStepConfinement(
|
|
757
|
+
schedule: Schedule,
|
|
758
|
+
stride: {start: number; interval: number; last: number}
|
|
759
|
+
): string {
|
|
760
|
+
const ordinal = minuteStepOrdinals[stride.interval];
|
|
761
|
+
const head = ordinal ?
|
|
762
|
+
'in jeder ' + ordinal + ' Minute' :
|
|
763
|
+
'alle ' + stride.interval + ' Minuten';
|
|
764
|
+
|
|
765
|
+
const tail = chooseStride({...stride, cycle: 60}, {
|
|
766
|
+
bare: () => '',
|
|
767
|
+
offset: () => ' ab Minute ' + stride.start,
|
|
768
|
+
bounded: () => ' von Minute ' + stride.start + ' bis ' + stride.last
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
return secondsLead(schedule) + ' ' + head + tail + ' jeder Stunde';
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// Whether a stepped minute fills a wildcard hour under a wildcard/stepped
|
|
775
|
+
// second — the shape the confinement frame above handles.
|
|
776
|
+
function isSteppedMinuteSeconds(
|
|
777
|
+
schedule: Schedule,
|
|
778
|
+
plan: Extract<PlanNode, {kind: 'composeSeconds'}>
|
|
779
|
+
): boolean {
|
|
780
|
+
return (plan.rest.kind === 'minuteFrequency' ||
|
|
781
|
+
plan.rest.kind === 'multipleMinutes') &&
|
|
782
|
+
(schedule.shapes.second === 'wildcard' ||
|
|
783
|
+
schedule.shapes.second === 'step') &&
|
|
784
|
+
schedule.shapes.hour === 'wildcard' &&
|
|
785
|
+
schedule.pattern.minute !== '*/2' &&
|
|
786
|
+
minuteStride(schedule) !== null;
|
|
787
|
+
}
|
|
788
|
+
|
|
720
789
|
function renderComposeSeconds(
|
|
721
790
|
schedule: Schedule,
|
|
722
791
|
plan: Extract<PlanNode, {kind: 'composeSeconds'}>,
|
|
@@ -738,16 +807,25 @@ function renderComposeSeconds(
|
|
|
738
807
|
}
|
|
739
808
|
}
|
|
740
809
|
|
|
741
|
-
// A
|
|
742
|
-
// clock-time rest would
|
|
743
|
-
//
|
|
744
|
-
// Bind the seconds into the explicit clock minute in the genitive
|
|
745
|
-
// Minute 9:
|
|
746
|
-
|
|
810
|
+
// A second over a single fixed minute and a specific hour is a single fixed
|
|
811
|
+
// timestamp: the clock-time rest would float the seconds as a separate
|
|
812
|
+
// apposition ("jede Sekunde, um 9:02 Uhr"), hiding that they belong to that
|
|
813
|
+
// one minute. Bind the seconds into the explicit clock minute in the genitive
|
|
814
|
+
// ("der Minute 9:02"), the same fusion the minute-0 case ("der Minute 9:00")
|
|
815
|
+
// uses; the recurring "täglich"/day frame is added in `describe`.
|
|
816
|
+
if (composeSingleMinute(schedule, plan)) {
|
|
747
817
|
return secondsLead(schedule) + ' ' +
|
|
748
818
|
clockMinuteGenitive(plan.rest.times, opts.style.sep);
|
|
749
819
|
}
|
|
750
820
|
|
|
821
|
+
// A stepped minute under a wildcard/stepped second + wildcard hour confines
|
|
822
|
+
// the second cadence to the ordinal minute cadence ("jede Sekunde in jeder
|
|
823
|
+
// sechsten Minute ab Minute 4 jeder Stunde"), never the comma juxtaposition
|
|
824
|
+
// that reads as two independent cadences.
|
|
825
|
+
if (isSteppedMinuteSeconds(schedule, plan)) {
|
|
826
|
+
return minuteStepConfinement(schedule, minuteStride(schedule)!);
|
|
827
|
+
}
|
|
828
|
+
|
|
751
829
|
// A wildcard second under a minute */2 with a wildcard hour binds in the
|
|
752
830
|
// genitive ("jede Sekunde jeder zweiten Minute").
|
|
753
831
|
if (isEveryOtherMinuteSeconds(schedule, plan)) {
|
|
@@ -765,21 +843,23 @@ function renderComposeSeconds(
|
|
|
765
843
|
return lead + render(schedule, plan.rest, opts);
|
|
766
844
|
}
|
|
767
845
|
|
|
768
|
-
// True when a compose-seconds plan is a sub-minute second over a
|
|
769
|
-
// clock-time rest — the
|
|
770
|
-
// the
|
|
771
|
-
|
|
846
|
+
// True when a compose-seconds plan is a sub-minute second over a single fixed
|
|
847
|
+
// minute's clock-time rest — the single fixed timestamp whose seconds must fuse
|
|
848
|
+
// to the explicit clock minute rather than float as a separate apposition.
|
|
849
|
+
// Minute 0 ("der Minute 0:00") is just this with the minute being 0; any single
|
|
850
|
+
// fixed minute fuses the same way.
|
|
851
|
+
function composeSingleMinute(
|
|
772
852
|
schedule: Schedule,
|
|
773
853
|
plan: Extract<PlanNode, {kind: 'composeSeconds'}>
|
|
774
854
|
): plan is Extract<PlanNode, {kind: 'composeSeconds'}> &
|
|
775
855
|
{rest: Extract<PlanNode, {kind: 'clockTimes'}>} {
|
|
776
856
|
return plan.rest.kind === 'clockTimes' &&
|
|
777
|
-
|
|
857
|
+
schedule.shapes.minute === 'single';
|
|
778
858
|
}
|
|
779
859
|
|
|
780
|
-
// The pinned clock minute in the genitive: "der Minute 9:
|
|
781
|
-
// "der Minuten 9:00, 10:00 und 17:00" for several — the explicit
|
|
782
|
-
// minute
|
|
860
|
+
// The pinned clock minute in the genitive: "der Minute 9:02" for one hour,
|
|
861
|
+
// "der Minuten 9:00, 10:00 und 17:00" for several — the explicit minute so the
|
|
862
|
+
// single-fixed-minute confinement stays visible.
|
|
783
863
|
function clockMinuteGenitive(
|
|
784
864
|
times: {hour: number; minute: number}[],
|
|
785
865
|
sep: string
|
|
@@ -940,18 +1020,22 @@ function renderMinuteFrequency(
|
|
|
940
1020
|
if (plan.hours.kind === 'during') {
|
|
941
1021
|
// A bounded or uneven hour stride confines the minute cadence to its own
|
|
942
1022
|
// endpoint-pinning hour cadence ("alle 15 Minuten, alle 5 Stunden von 0 bis
|
|
943
|
-
// 20 Uhr").
|
|
1023
|
+
// 20 Uhr"). That hour step is the sole hour authority, so an offset minute
|
|
1024
|
+
// cadence drops its generic "jeder Stunde" (an every-hour scope conflicting
|
|
1025
|
+
// with the step); a list of specific hours keeps it.
|
|
944
1026
|
const cadence = unevenHourCadence(schedule);
|
|
945
1027
|
|
|
946
1028
|
return cadence ?
|
|
947
|
-
|
|
1029
|
+
stepClause(segment, UNITS.minute, '') + ', ' + cadence :
|
|
948
1030
|
base + ' ' + duringHours(schedule, plan.hours.times, sep);
|
|
949
1031
|
}
|
|
950
1032
|
|
|
951
1033
|
if (plan.hours.kind === 'step') {
|
|
952
1034
|
// The plan carries a step only for a clean step (dividing the day):
|
|
953
|
-
// confine the cadence to every Nth hour ("in jeder zweiten Stunde").
|
|
954
|
-
|
|
1035
|
+
// confine the cadence to every Nth hour ("in jeder zweiten Stunde"). The
|
|
1036
|
+
// hour step is the sole hour authority, so the minute cadence drops its
|
|
1037
|
+
// generic "jeder Stunde".
|
|
1038
|
+
return stepClause(segment, UNITS.minute, '') + ' ' +
|
|
955
1039
|
everyNthHour(stepSegment(schedule, 'hour'));
|
|
956
1040
|
}
|
|
957
1041
|
|
|
@@ -1405,28 +1489,31 @@ function qualifier(schedule: Schedule, months: Months): string {
|
|
|
1405
1489
|
|
|
1406
1490
|
// Plan kinds whose clause is a clock time: the qualifier leads them ("montags
|
|
1407
1491
|
// um 9 Uhr"); a frequency clause trails it ("jede Minute montags"). The
|
|
1408
|
-
// minute
|
|
1409
|
-
// qualifier leads it ("montags jede Sekunde der Minute 9:00").
|
|
1492
|
+
// single-fixed-minute compose-seconds clause is anchored on a clock minute too,
|
|
1493
|
+
// so the qualifier leads it ("montags jede Sekunde der Minute 9:00").
|
|
1410
1494
|
const LEADING_PLANS = new Set(['clockTimes']);
|
|
1411
1495
|
|
|
1412
1496
|
// True when the leading qualifier should precede the clause: a clock-time
|
|
1413
|
-
// plan, or the minute
|
|
1497
|
+
// plan, or the single-fixed-minute compose-seconds clause that surfaces a clock
|
|
1498
|
+
// minute.
|
|
1414
1499
|
function leadsQualifier(schedule: Schedule): boolean {
|
|
1415
|
-
return LEADING_PLANS.has(schedule.plan.kind) ||
|
|
1500
|
+
return LEADING_PLANS.has(schedule.plan.kind) ||
|
|
1501
|
+
isComposeSingleMinute(schedule);
|
|
1416
1502
|
}
|
|
1417
1503
|
|
|
1418
|
-
// Whether the planned clause is the minute
|
|
1419
|
-
// (a sub-minute second over a minute
|
|
1420
|
-
|
|
1504
|
+
// Whether the planned clause is the single-fixed-minute compose-seconds
|
|
1505
|
+
// confinement (a sub-minute second over a single fixed minute's clock-time
|
|
1506
|
+
// rest).
|
|
1507
|
+
function isComposeSingleMinute(schedule: Schedule): boolean {
|
|
1421
1508
|
return schedule.plan.kind === 'composeSeconds' &&
|
|
1422
|
-
|
|
1509
|
+
composeSingleMinute(schedule, schedule.plan);
|
|
1423
1510
|
}
|
|
1424
1511
|
|
|
1425
1512
|
// True when the clause is a bare daily clock-time list and so needs the
|
|
1426
1513
|
// "täglich" frame to read as recurring, not a one-off: clockTimes always, the
|
|
1427
|
-
// minute
|
|
1428
|
-
// hour step (rendered as its fire list "um 0, 5, … Uhr", not the cadence
|
|
1429
|
-
// N Stunden"). A frequency clause already implies recurrence.
|
|
1514
|
+
// single-fixed-minute compose-seconds clause (a recurring clock minute), and an
|
|
1515
|
+
// uneven hour step (rendered as its fire list "um 0, 5, … Uhr", not the cadence
|
|
1516
|
+
// "alle N Stunden"). A frequency clause already implies recurrence.
|
|
1430
1517
|
function needsDailyFrame(schedule: Schedule): boolean {
|
|
1431
1518
|
// An hour cadence is a sub-daily frequency, not a daily clock-time list, so
|
|
1432
1519
|
// it must not take the "täglich" frame ("alle 2 Stunden", not "täglich alle
|
|
@@ -1435,7 +1522,7 @@ function needsDailyFrame(schedule: Schedule): boolean {
|
|
|
1435
1522
|
return false;
|
|
1436
1523
|
}
|
|
1437
1524
|
|
|
1438
|
-
if (schedule.plan.kind === 'clockTimes' ||
|
|
1525
|
+
if (schedule.plan.kind === 'clockTimes' || isComposeSingleMinute(schedule)) {
|
|
1439
1526
|
return true;
|
|
1440
1527
|
}
|
|
1441
1528
|
|