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/fi.cjs
CHANGED
|
@@ -72,6 +72,26 @@ function arithmeticStep(values) {
|
|
|
72
72
|
}
|
|
73
73
|
return { start: values[0], interval, last: values[values.length - 1] };
|
|
74
74
|
}
|
|
75
|
+
function weekdayDisplayKey(value) {
|
|
76
|
+
return value === 0 ? 7 : value;
|
|
77
|
+
}
|
|
78
|
+
function orderWeekdaysForDisplay(segments) {
|
|
79
|
+
const flattened = segments.flatMap(function flat(segment) {
|
|
80
|
+
return segment.kind === "step" ? segment.fires.map(function single(value) {
|
|
81
|
+
return { kind: "single", value: "" + value };
|
|
82
|
+
}) : [segment];
|
|
83
|
+
});
|
|
84
|
+
function key(segment) {
|
|
85
|
+
return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
|
|
86
|
+
}
|
|
87
|
+
return flattened.map(function index(segment, position) {
|
|
88
|
+
return [segment, position];
|
|
89
|
+
}).sort(function byDisplayKey(a, b) {
|
|
90
|
+
return key(a[0]) - key(b[0]) || a[1] - b[1];
|
|
91
|
+
}).map(function unwrap(pair) {
|
|
92
|
+
return pair[0];
|
|
93
|
+
});
|
|
94
|
+
}
|
|
75
95
|
function toFieldNumber(token, numberMap) {
|
|
76
96
|
return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
|
|
77
97
|
}
|
|
@@ -289,7 +309,11 @@ function composeSecondsOverMinuteStep(ir, freq, opts) {
|
|
|
289
309
|
}
|
|
290
310
|
function composeHourCadence(ir, plan, opts) {
|
|
291
311
|
const clockRest = plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes";
|
|
292
|
-
|
|
312
|
+
if (!clockRest || ir.shapes.minute !== "single") {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
const minute = +ir.pattern.minute;
|
|
316
|
+
return hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
|
|
293
317
|
}
|
|
294
318
|
function renderComposeSeconds(ir, plan, opts) {
|
|
295
319
|
const cadence = composeHourCadence(ir, plan, opts);
|
|
@@ -305,7 +329,9 @@ function renderComposeSeconds(ir, plan, opts) {
|
|
|
305
329
|
if (isEveryOtherMinuteSeconds(ir, plan)) {
|
|
306
330
|
return secondsLeadClause(ir, opts) + " joka toisena minuuttina";
|
|
307
331
|
}
|
|
308
|
-
|
|
332
|
+
const restOwnsLead = plan.rest.kind === "compactClockTimes" && ir.analyses.clockSecond;
|
|
333
|
+
const lead = restOwnsLead ? "" : secondsLeadClause(ir, opts) + ", ";
|
|
334
|
+
return lead + render(ir, plan.rest, opts);
|
|
309
335
|
}
|
|
310
336
|
function isEveryOtherMinuteSeconds(ir, plan) {
|
|
311
337
|
if (plan.rest.kind !== "minuteFrequency" || ir.pattern.second !== "*" || ir.shapes.hour !== "wildcard") {
|
|
@@ -424,6 +450,10 @@ function hourSegmentTimesWithSeka(ir, minute, second, opts) {
|
|
|
424
450
|
function renderMinuteFrequency(ir, plan, opts) {
|
|
425
451
|
const seg = stepSegment(ir.analyses.segments.minute);
|
|
426
452
|
if (plan.hours.kind === "during") {
|
|
453
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
454
|
+
if (cadence !== null) {
|
|
455
|
+
return stepCycle60(seg, units.minute, opts) + ", " + cadence + trailingQualifier(ir, opts);
|
|
456
|
+
}
|
|
427
457
|
if (minuteStepIsAnchored(seg)) {
|
|
428
458
|
const bareHours = kloFromTimes(ir, plan.hours.times, opts);
|
|
429
459
|
return hoursFirstMinutes(bareHours, ir, opts) + trailingQualifier(ir, opts);
|
|
@@ -449,8 +479,12 @@ function renderMinuteSpanInHour(ir, plan, opts) {
|
|
|
449
479
|
) + trailingQualifier(ir, opts);
|
|
450
480
|
}
|
|
451
481
|
function renderMinutesAcrossHours(ir, plan, opts) {
|
|
482
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
452
483
|
if (plan.form === "wildcard") {
|
|
453
|
-
return "joka minuutti " + hourWindowsFromTimes(ir, plan.times, opts) + trailingQualifier(ir, opts);
|
|
484
|
+
return cadence ? "joka minuutti, " + cadence + trailingQualifier(ir, opts) : "joka minuutti " + hourWindowsFromTimes(ir, plan.times, opts) + trailingQualifier(ir, opts);
|
|
485
|
+
}
|
|
486
|
+
if (cadence !== null) {
|
|
487
|
+
return bareMinutes(ir, opts) + ", " + cadence + trailingQualifier(ir, opts);
|
|
454
488
|
}
|
|
455
489
|
if (hoursAreRangeIsolated(ir.analyses.segments.hour)) {
|
|
456
490
|
return bareMinutes(ir, opts) + " " + hourSegmentTimesWithSeka(ir, 0, null, opts) + trailingQualifier(ir, opts);
|
|
@@ -460,12 +494,12 @@ function renderMinutesAcrossHours(ir, plan, opts) {
|
|
|
460
494
|
}
|
|
461
495
|
function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
|
|
462
496
|
const segment = stepSegment(ir.analyses.segments.hour);
|
|
497
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
463
498
|
if (plan.form === "wildcard") {
|
|
464
499
|
return "joka minuutti " + everyNthHour(segment, opts) + trailingQualifier(ir, opts);
|
|
465
500
|
}
|
|
466
|
-
if (
|
|
467
|
-
|
|
468
|
-
return hoursFirstMinutes(hoursStr, ir, opts) + trailingQualifier(ir, opts);
|
|
501
|
+
if (cadence !== null) {
|
|
502
|
+
return bareMinutes(ir, opts) + ", " + cadence + trailingQualifier(ir, opts);
|
|
469
503
|
}
|
|
470
504
|
return bareMinutes(ir, opts) + hourStepTail(segment, opts) + trailingQualifier(ir, opts);
|
|
471
505
|
}
|
|
@@ -540,6 +574,10 @@ function renderHourRange(ir, plan, opts) {
|
|
|
540
574
|
return hoursFirstMinutes(window, ir, opts) + trailingQualifier(ir, opts);
|
|
541
575
|
}
|
|
542
576
|
function renderHourStep(ir, plan, opts) {
|
|
577
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
578
|
+
if (cadence !== null) {
|
|
579
|
+
return cadence + trailingQualifier(ir, opts);
|
|
580
|
+
}
|
|
543
581
|
return stepHours(stepSegment(ir.analyses.segments.hour), opts) + trailingQualifier(ir, opts);
|
|
544
582
|
}
|
|
545
583
|
function boundedWindow(plan) {
|
|
@@ -554,7 +592,8 @@ function hourWindow(window, opts) {
|
|
|
554
592
|
}
|
|
555
593
|
function renderClockTimes(ir, plan, opts) {
|
|
556
594
|
if (ir.shapes.minute === "single") {
|
|
557
|
-
const
|
|
595
|
+
const minute = +ir.pattern.minute;
|
|
596
|
+
const cadence = hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
|
|
558
597
|
if (cadence !== null) {
|
|
559
598
|
return cadence;
|
|
560
599
|
}
|
|
@@ -570,9 +609,9 @@ function renderClockTimes(ir, plan, opts) {
|
|
|
570
609
|
}
|
|
571
610
|
function renderCompactClockTimes(ir, plan, opts) {
|
|
572
611
|
if (plan.fold) {
|
|
573
|
-
const
|
|
574
|
-
if (
|
|
575
|
-
return
|
|
612
|
+
const cadence2 = hourCadence(ir, plan.minute, opts) ?? hourRangeCadence(ir, plan.minute, opts);
|
|
613
|
+
if (cadence2 !== null) {
|
|
614
|
+
return cadence2;
|
|
576
615
|
}
|
|
577
616
|
}
|
|
578
617
|
const hourSegs = ir.analyses.segments.hour;
|
|
@@ -591,8 +630,12 @@ function renderCompactClockTimes(ir, plan, opts) {
|
|
|
591
630
|
if (plan.fold) {
|
|
592
631
|
return leadingQualifier(ir, opts) + hourSegmentTimes(ir, plan.minute, ir.analyses.clockSecond, opts);
|
|
593
632
|
}
|
|
594
|
-
const
|
|
595
|
-
const phrase =
|
|
633
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
634
|
+
const phrase = cadence ? bareMinutes(ir, opts) + ", " + cadence + trailingQualifier(ir, opts) : (
|
|
635
|
+
// A minute list over purely enumerated hours (step fires, all singles) —
|
|
636
|
+
// hours-first, drop "joka tunti".
|
|
637
|
+
hoursFirstMinutes(hourSegmentTimes(ir, 0, null, opts), ir, opts) + trailingQualifier(ir, opts)
|
|
638
|
+
);
|
|
596
639
|
return ir.analyses.clockSecond ? secondsLeadClause(ir, opts) + ", " + phrase : phrase;
|
|
597
640
|
}
|
|
598
641
|
var renderers = {
|
|
@@ -685,6 +728,27 @@ function hourStrideCadence(stride, opts) {
|
|
|
685
728
|
}
|
|
686
729
|
return cadence + " " + kloRange({ hour: start, minute: 0 }, { hour: last, minute: 0 }, opts);
|
|
687
730
|
}
|
|
731
|
+
function hourListStride(values) {
|
|
732
|
+
if (values.length < 2) {
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
const interval = values[1] - values[0];
|
|
736
|
+
if (interval < 2) {
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
for (let i = 2; i < values.length; i += 1) {
|
|
740
|
+
if (values[i] - values[i - 1] !== interval) {
|
|
741
|
+
return null;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (values[0] !== 0 && values.length < 5) {
|
|
745
|
+
return null;
|
|
746
|
+
}
|
|
747
|
+
return { interval, last: values[values.length - 1], start: values[0] };
|
|
748
|
+
}
|
|
749
|
+
function offsetCleanStride(stride) {
|
|
750
|
+
return stride.start < stride.interval && 24 % stride.interval === 0;
|
|
751
|
+
}
|
|
688
752
|
function hourStride(ir) {
|
|
689
753
|
const segments = ir.analyses.segments.hour;
|
|
690
754
|
if (!segments) {
|
|
@@ -692,12 +756,21 @@ function hourStride(ir) {
|
|
|
692
756
|
}
|
|
693
757
|
if (segments.length === 1 && segments[0].kind === "step") {
|
|
694
758
|
const segment = segments[0];
|
|
759
|
+
if (segment.fires.length < 2) {
|
|
760
|
+
return null;
|
|
761
|
+
}
|
|
695
762
|
const start = segment.startToken === "*" ? 0 : +segment.startToken.split("-")[0];
|
|
696
763
|
return { interval: segment.interval, last: segment.fires[segment.fires.length - 1], start };
|
|
697
764
|
}
|
|
698
765
|
const values = singleValues(segments);
|
|
699
|
-
|
|
700
|
-
|
|
766
|
+
return values && hourListStride(values);
|
|
767
|
+
}
|
|
768
|
+
function unevenHourCadence(ir, opts) {
|
|
769
|
+
const stride = hourStride(ir);
|
|
770
|
+
if (!stride || offsetCleanStride(stride)) {
|
|
771
|
+
return null;
|
|
772
|
+
}
|
|
773
|
+
return hourStrideCadence(stride, opts);
|
|
701
774
|
}
|
|
702
775
|
function subMinuteSecond(ir) {
|
|
703
776
|
return ir.pattern.second === "*" || ir.shapes.second === "step";
|
|
@@ -721,7 +794,7 @@ function hourCadence(ir, minute, opts) {
|
|
|
721
794
|
return null;
|
|
722
795
|
}
|
|
723
796
|
const fires = (stride.last - stride.start) / stride.interval + 1;
|
|
724
|
-
if (ir.pattern.second === "0" && fires <= maxClockTimes) {
|
|
797
|
+
if (ir.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
|
|
725
798
|
return null;
|
|
726
799
|
}
|
|
727
800
|
const segment = ir.analyses.segments.hour[0];
|
|
@@ -729,6 +802,9 @@ function hourCadence(ir, minute, opts) {
|
|
|
729
802
|
if (confined) {
|
|
730
803
|
return secondsLeadClause(ir, opts) + " minuutin ajan " + everyNthHour(segment, opts) + trailingQualifier(ir, opts);
|
|
731
804
|
}
|
|
805
|
+
if (minute === 0 && ir.pattern.second === "0") {
|
|
806
|
+
return hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
|
|
807
|
+
}
|
|
732
808
|
return hourCadenceLead(ir, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
|
|
733
809
|
}
|
|
734
810
|
function cleanHourStride(segment) {
|
|
@@ -738,6 +814,23 @@ function cleanHourStride(segment) {
|
|
|
738
814
|
const start = segment.startToken === "*" ? 0 : +segment.startToken;
|
|
739
815
|
return 24 % segment.interval === 0 && start < segment.interval;
|
|
740
816
|
}
|
|
817
|
+
function hasHourWindow(ir) {
|
|
818
|
+
const segments = ir.analyses.segments.hour;
|
|
819
|
+
return !!segments && segments.some(function range(segment) {
|
|
820
|
+
return segment.kind === "range";
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
function hourRangeWindowTail(ir, opts) {
|
|
824
|
+
return ir.analyses.segments.hour.length === 1 ? hourSegmentTimes(ir, 0, null, opts) : hourSegmentTimesWithSeka(ir, 0, null, opts);
|
|
825
|
+
}
|
|
826
|
+
function hourRangeCadence(ir, minute, opts) {
|
|
827
|
+
if (minute !== 0 || !hasHourWindow(ir) || ir.pattern.second === "0") {
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
const tail = hourRangeWindowTail(ir, opts);
|
|
831
|
+
const joiner = subMinuteSecond(ir) ? " " : ", ";
|
|
832
|
+
return hourCadenceLead(ir, minute, opts) + joiner + tail + trailingQualifier(ir, opts);
|
|
833
|
+
}
|
|
741
834
|
function kloList(hours, opts) {
|
|
742
835
|
if (hours.length === 1) {
|
|
743
836
|
return timeWord(hours[0], 0, null, opts);
|
|
@@ -754,12 +847,16 @@ function kloFromTimes(ir, times, opts) {
|
|
|
754
847
|
}
|
|
755
848
|
function hourWindowsFromTimes(ir, times, opts) {
|
|
756
849
|
if (times.kind === "fires") {
|
|
757
|
-
return
|
|
758
|
-
|
|
759
|
-
|
|
850
|
+
return kloList(times.fires, opts);
|
|
851
|
+
}
|
|
852
|
+
const segments = ir.analyses.segments.hour;
|
|
853
|
+
if (!segments.some(function ranged(segment) {
|
|
854
|
+
return segment.kind === "range";
|
|
855
|
+
})) {
|
|
856
|
+
return kloList(hourSegmentFires(segments), opts);
|
|
760
857
|
}
|
|
761
858
|
const pieces = [];
|
|
762
|
-
|
|
859
|
+
segments.forEach(function window(segment) {
|
|
763
860
|
if (segment.kind === "range") {
|
|
764
861
|
pieces.push(rangeDigits(
|
|
765
862
|
{ hour: +segment.bounds[0], minute: 0 },
|
|
@@ -776,6 +873,17 @@ function hourWindowsFromTimes(ir, times, opts) {
|
|
|
776
873
|
});
|
|
777
874
|
return "klo " + joinList(pieces);
|
|
778
875
|
}
|
|
876
|
+
function hourSegmentFires(segments) {
|
|
877
|
+
const hours = [];
|
|
878
|
+
segments.forEach(function each(segment) {
|
|
879
|
+
if (segment.kind === "step") {
|
|
880
|
+
hours.push(...segment.fires);
|
|
881
|
+
} else if (segment.kind === "single") {
|
|
882
|
+
hours.push(+segment.value);
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
return hours;
|
|
886
|
+
}
|
|
779
887
|
function hourWindowDigits(hour, opts) {
|
|
780
888
|
return rangeDigits({ hour, minute: 0 }, { hour, minute: 59 }, opts);
|
|
781
889
|
}
|
|
@@ -877,7 +985,7 @@ function weekdayQualifier(ir) {
|
|
|
877
985
|
if (quartz) {
|
|
878
986
|
return quartz;
|
|
879
987
|
}
|
|
880
|
-
const segments =
|
|
988
|
+
const segments = orderWeekdaysForDisplay(ir.analyses.segments.weekday);
|
|
881
989
|
return joinList(segments.map(function piece(segment) {
|
|
882
990
|
if (segment.kind === "range") {
|
|
883
991
|
return weekdays[weekdayNumber(segment.bounds[0])].ela + " " + weekdays[weekdayNumber(segment.bounds[1])].ill;
|
package/dist/lang/fi.js
CHANGED
|
@@ -46,6 +46,26 @@ function arithmeticStep(values) {
|
|
|
46
46
|
}
|
|
47
47
|
return { start: values[0], interval, last: values[values.length - 1] };
|
|
48
48
|
}
|
|
49
|
+
function weekdayDisplayKey(value) {
|
|
50
|
+
return value === 0 ? 7 : value;
|
|
51
|
+
}
|
|
52
|
+
function orderWeekdaysForDisplay(segments) {
|
|
53
|
+
const flattened = segments.flatMap(function flat(segment) {
|
|
54
|
+
return segment.kind === "step" ? segment.fires.map(function single(value) {
|
|
55
|
+
return { kind: "single", value: "" + value };
|
|
56
|
+
}) : [segment];
|
|
57
|
+
});
|
|
58
|
+
function key(segment) {
|
|
59
|
+
return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
|
|
60
|
+
}
|
|
61
|
+
return flattened.map(function index(segment, position) {
|
|
62
|
+
return [segment, position];
|
|
63
|
+
}).sort(function byDisplayKey(a, b) {
|
|
64
|
+
return key(a[0]) - key(b[0]) || a[1] - b[1];
|
|
65
|
+
}).map(function unwrap(pair) {
|
|
66
|
+
return pair[0];
|
|
67
|
+
});
|
|
68
|
+
}
|
|
49
69
|
function toFieldNumber(token, numberMap) {
|
|
50
70
|
return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
|
|
51
71
|
}
|
|
@@ -263,7 +283,11 @@ function composeSecondsOverMinuteStep(ir, freq, opts) {
|
|
|
263
283
|
}
|
|
264
284
|
function composeHourCadence(ir, plan, opts) {
|
|
265
285
|
const clockRest = plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes";
|
|
266
|
-
|
|
286
|
+
if (!clockRest || ir.shapes.minute !== "single") {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
const minute = +ir.pattern.minute;
|
|
290
|
+
return hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
|
|
267
291
|
}
|
|
268
292
|
function renderComposeSeconds(ir, plan, opts) {
|
|
269
293
|
const cadence = composeHourCadence(ir, plan, opts);
|
|
@@ -279,7 +303,9 @@ function renderComposeSeconds(ir, plan, opts) {
|
|
|
279
303
|
if (isEveryOtherMinuteSeconds(ir, plan)) {
|
|
280
304
|
return secondsLeadClause(ir, opts) + " joka toisena minuuttina";
|
|
281
305
|
}
|
|
282
|
-
|
|
306
|
+
const restOwnsLead = plan.rest.kind === "compactClockTimes" && ir.analyses.clockSecond;
|
|
307
|
+
const lead = restOwnsLead ? "" : secondsLeadClause(ir, opts) + ", ";
|
|
308
|
+
return lead + render(ir, plan.rest, opts);
|
|
283
309
|
}
|
|
284
310
|
function isEveryOtherMinuteSeconds(ir, plan) {
|
|
285
311
|
if (plan.rest.kind !== "minuteFrequency" || ir.pattern.second !== "*" || ir.shapes.hour !== "wildcard") {
|
|
@@ -398,6 +424,10 @@ function hourSegmentTimesWithSeka(ir, minute, second, opts) {
|
|
|
398
424
|
function renderMinuteFrequency(ir, plan, opts) {
|
|
399
425
|
const seg = stepSegment(ir.analyses.segments.minute);
|
|
400
426
|
if (plan.hours.kind === "during") {
|
|
427
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
428
|
+
if (cadence !== null) {
|
|
429
|
+
return stepCycle60(seg, units.minute, opts) + ", " + cadence + trailingQualifier(ir, opts);
|
|
430
|
+
}
|
|
401
431
|
if (minuteStepIsAnchored(seg)) {
|
|
402
432
|
const bareHours = kloFromTimes(ir, plan.hours.times, opts);
|
|
403
433
|
return hoursFirstMinutes(bareHours, ir, opts) + trailingQualifier(ir, opts);
|
|
@@ -423,8 +453,12 @@ function renderMinuteSpanInHour(ir, plan, opts) {
|
|
|
423
453
|
) + trailingQualifier(ir, opts);
|
|
424
454
|
}
|
|
425
455
|
function renderMinutesAcrossHours(ir, plan, opts) {
|
|
456
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
426
457
|
if (plan.form === "wildcard") {
|
|
427
|
-
return "joka minuutti " + hourWindowsFromTimes(ir, plan.times, opts) + trailingQualifier(ir, opts);
|
|
458
|
+
return cadence ? "joka minuutti, " + cadence + trailingQualifier(ir, opts) : "joka minuutti " + hourWindowsFromTimes(ir, plan.times, opts) + trailingQualifier(ir, opts);
|
|
459
|
+
}
|
|
460
|
+
if (cadence !== null) {
|
|
461
|
+
return bareMinutes(ir, opts) + ", " + cadence + trailingQualifier(ir, opts);
|
|
428
462
|
}
|
|
429
463
|
if (hoursAreRangeIsolated(ir.analyses.segments.hour)) {
|
|
430
464
|
return bareMinutes(ir, opts) + " " + hourSegmentTimesWithSeka(ir, 0, null, opts) + trailingQualifier(ir, opts);
|
|
@@ -434,12 +468,12 @@ function renderMinutesAcrossHours(ir, plan, opts) {
|
|
|
434
468
|
}
|
|
435
469
|
function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
|
|
436
470
|
const segment = stepSegment(ir.analyses.segments.hour);
|
|
471
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
437
472
|
if (plan.form === "wildcard") {
|
|
438
473
|
return "joka minuutti " + everyNthHour(segment, opts) + trailingQualifier(ir, opts);
|
|
439
474
|
}
|
|
440
|
-
if (
|
|
441
|
-
|
|
442
|
-
return hoursFirstMinutes(hoursStr, ir, opts) + trailingQualifier(ir, opts);
|
|
475
|
+
if (cadence !== null) {
|
|
476
|
+
return bareMinutes(ir, opts) + ", " + cadence + trailingQualifier(ir, opts);
|
|
443
477
|
}
|
|
444
478
|
return bareMinutes(ir, opts) + hourStepTail(segment, opts) + trailingQualifier(ir, opts);
|
|
445
479
|
}
|
|
@@ -514,6 +548,10 @@ function renderHourRange(ir, plan, opts) {
|
|
|
514
548
|
return hoursFirstMinutes(window, ir, opts) + trailingQualifier(ir, opts);
|
|
515
549
|
}
|
|
516
550
|
function renderHourStep(ir, plan, opts) {
|
|
551
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
552
|
+
if (cadence !== null) {
|
|
553
|
+
return cadence + trailingQualifier(ir, opts);
|
|
554
|
+
}
|
|
517
555
|
return stepHours(stepSegment(ir.analyses.segments.hour), opts) + trailingQualifier(ir, opts);
|
|
518
556
|
}
|
|
519
557
|
function boundedWindow(plan) {
|
|
@@ -528,7 +566,8 @@ function hourWindow(window, opts) {
|
|
|
528
566
|
}
|
|
529
567
|
function renderClockTimes(ir, plan, opts) {
|
|
530
568
|
if (ir.shapes.minute === "single") {
|
|
531
|
-
const
|
|
569
|
+
const minute = +ir.pattern.minute;
|
|
570
|
+
const cadence = hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
|
|
532
571
|
if (cadence !== null) {
|
|
533
572
|
return cadence;
|
|
534
573
|
}
|
|
@@ -544,9 +583,9 @@ function renderClockTimes(ir, plan, opts) {
|
|
|
544
583
|
}
|
|
545
584
|
function renderCompactClockTimes(ir, plan, opts) {
|
|
546
585
|
if (plan.fold) {
|
|
547
|
-
const
|
|
548
|
-
if (
|
|
549
|
-
return
|
|
586
|
+
const cadence2 = hourCadence(ir, plan.minute, opts) ?? hourRangeCadence(ir, plan.minute, opts);
|
|
587
|
+
if (cadence2 !== null) {
|
|
588
|
+
return cadence2;
|
|
550
589
|
}
|
|
551
590
|
}
|
|
552
591
|
const hourSegs = ir.analyses.segments.hour;
|
|
@@ -565,8 +604,12 @@ function renderCompactClockTimes(ir, plan, opts) {
|
|
|
565
604
|
if (plan.fold) {
|
|
566
605
|
return leadingQualifier(ir, opts) + hourSegmentTimes(ir, plan.minute, ir.analyses.clockSecond, opts);
|
|
567
606
|
}
|
|
568
|
-
const
|
|
569
|
-
const phrase =
|
|
607
|
+
const cadence = unevenHourCadence(ir, opts);
|
|
608
|
+
const phrase = cadence ? bareMinutes(ir, opts) + ", " + cadence + trailingQualifier(ir, opts) : (
|
|
609
|
+
// A minute list over purely enumerated hours (step fires, all singles) —
|
|
610
|
+
// hours-first, drop "joka tunti".
|
|
611
|
+
hoursFirstMinutes(hourSegmentTimes(ir, 0, null, opts), ir, opts) + trailingQualifier(ir, opts)
|
|
612
|
+
);
|
|
570
613
|
return ir.analyses.clockSecond ? secondsLeadClause(ir, opts) + ", " + phrase : phrase;
|
|
571
614
|
}
|
|
572
615
|
var renderers = {
|
|
@@ -659,6 +702,27 @@ function hourStrideCadence(stride, opts) {
|
|
|
659
702
|
}
|
|
660
703
|
return cadence + " " + kloRange({ hour: start, minute: 0 }, { hour: last, minute: 0 }, opts);
|
|
661
704
|
}
|
|
705
|
+
function hourListStride(values) {
|
|
706
|
+
if (values.length < 2) {
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
const interval = values[1] - values[0];
|
|
710
|
+
if (interval < 2) {
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
for (let i = 2; i < values.length; i += 1) {
|
|
714
|
+
if (values[i] - values[i - 1] !== interval) {
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (values[0] !== 0 && values.length < 5) {
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
return { interval, last: values[values.length - 1], start: values[0] };
|
|
722
|
+
}
|
|
723
|
+
function offsetCleanStride(stride) {
|
|
724
|
+
return stride.start < stride.interval && 24 % stride.interval === 0;
|
|
725
|
+
}
|
|
662
726
|
function hourStride(ir) {
|
|
663
727
|
const segments = ir.analyses.segments.hour;
|
|
664
728
|
if (!segments) {
|
|
@@ -666,12 +730,21 @@ function hourStride(ir) {
|
|
|
666
730
|
}
|
|
667
731
|
if (segments.length === 1 && segments[0].kind === "step") {
|
|
668
732
|
const segment = segments[0];
|
|
733
|
+
if (segment.fires.length < 2) {
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
669
736
|
const start = segment.startToken === "*" ? 0 : +segment.startToken.split("-")[0];
|
|
670
737
|
return { interval: segment.interval, last: segment.fires[segment.fires.length - 1], start };
|
|
671
738
|
}
|
|
672
739
|
const values = singleValues(segments);
|
|
673
|
-
|
|
674
|
-
|
|
740
|
+
return values && hourListStride(values);
|
|
741
|
+
}
|
|
742
|
+
function unevenHourCadence(ir, opts) {
|
|
743
|
+
const stride = hourStride(ir);
|
|
744
|
+
if (!stride || offsetCleanStride(stride)) {
|
|
745
|
+
return null;
|
|
746
|
+
}
|
|
747
|
+
return hourStrideCadence(stride, opts);
|
|
675
748
|
}
|
|
676
749
|
function subMinuteSecond(ir) {
|
|
677
750
|
return ir.pattern.second === "*" || ir.shapes.second === "step";
|
|
@@ -695,7 +768,7 @@ function hourCadence(ir, minute, opts) {
|
|
|
695
768
|
return null;
|
|
696
769
|
}
|
|
697
770
|
const fires = (stride.last - stride.start) / stride.interval + 1;
|
|
698
|
-
if (ir.pattern.second === "0" && fires <= maxClockTimes) {
|
|
771
|
+
if (ir.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
|
|
699
772
|
return null;
|
|
700
773
|
}
|
|
701
774
|
const segment = ir.analyses.segments.hour[0];
|
|
@@ -703,6 +776,9 @@ function hourCadence(ir, minute, opts) {
|
|
|
703
776
|
if (confined) {
|
|
704
777
|
return secondsLeadClause(ir, opts) + " minuutin ajan " + everyNthHour(segment, opts) + trailingQualifier(ir, opts);
|
|
705
778
|
}
|
|
779
|
+
if (minute === 0 && ir.pattern.second === "0") {
|
|
780
|
+
return hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
|
|
781
|
+
}
|
|
706
782
|
return hourCadenceLead(ir, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
|
|
707
783
|
}
|
|
708
784
|
function cleanHourStride(segment) {
|
|
@@ -712,6 +788,23 @@ function cleanHourStride(segment) {
|
|
|
712
788
|
const start = segment.startToken === "*" ? 0 : +segment.startToken;
|
|
713
789
|
return 24 % segment.interval === 0 && start < segment.interval;
|
|
714
790
|
}
|
|
791
|
+
function hasHourWindow(ir) {
|
|
792
|
+
const segments = ir.analyses.segments.hour;
|
|
793
|
+
return !!segments && segments.some(function range(segment) {
|
|
794
|
+
return segment.kind === "range";
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
function hourRangeWindowTail(ir, opts) {
|
|
798
|
+
return ir.analyses.segments.hour.length === 1 ? hourSegmentTimes(ir, 0, null, opts) : hourSegmentTimesWithSeka(ir, 0, null, opts);
|
|
799
|
+
}
|
|
800
|
+
function hourRangeCadence(ir, minute, opts) {
|
|
801
|
+
if (minute !== 0 || !hasHourWindow(ir) || ir.pattern.second === "0") {
|
|
802
|
+
return null;
|
|
803
|
+
}
|
|
804
|
+
const tail = hourRangeWindowTail(ir, opts);
|
|
805
|
+
const joiner = subMinuteSecond(ir) ? " " : ", ";
|
|
806
|
+
return hourCadenceLead(ir, minute, opts) + joiner + tail + trailingQualifier(ir, opts);
|
|
807
|
+
}
|
|
715
808
|
function kloList(hours, opts) {
|
|
716
809
|
if (hours.length === 1) {
|
|
717
810
|
return timeWord(hours[0], 0, null, opts);
|
|
@@ -728,12 +821,16 @@ function kloFromTimes(ir, times, opts) {
|
|
|
728
821
|
}
|
|
729
822
|
function hourWindowsFromTimes(ir, times, opts) {
|
|
730
823
|
if (times.kind === "fires") {
|
|
731
|
-
return
|
|
732
|
-
|
|
733
|
-
|
|
824
|
+
return kloList(times.fires, opts);
|
|
825
|
+
}
|
|
826
|
+
const segments = ir.analyses.segments.hour;
|
|
827
|
+
if (!segments.some(function ranged(segment) {
|
|
828
|
+
return segment.kind === "range";
|
|
829
|
+
})) {
|
|
830
|
+
return kloList(hourSegmentFires(segments), opts);
|
|
734
831
|
}
|
|
735
832
|
const pieces = [];
|
|
736
|
-
|
|
833
|
+
segments.forEach(function window(segment) {
|
|
737
834
|
if (segment.kind === "range") {
|
|
738
835
|
pieces.push(rangeDigits(
|
|
739
836
|
{ hour: +segment.bounds[0], minute: 0 },
|
|
@@ -750,6 +847,17 @@ function hourWindowsFromTimes(ir, times, opts) {
|
|
|
750
847
|
});
|
|
751
848
|
return "klo " + joinList(pieces);
|
|
752
849
|
}
|
|
850
|
+
function hourSegmentFires(segments) {
|
|
851
|
+
const hours = [];
|
|
852
|
+
segments.forEach(function each(segment) {
|
|
853
|
+
if (segment.kind === "step") {
|
|
854
|
+
hours.push(...segment.fires);
|
|
855
|
+
} else if (segment.kind === "single") {
|
|
856
|
+
hours.push(+segment.value);
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
return hours;
|
|
860
|
+
}
|
|
753
861
|
function hourWindowDigits(hour, opts) {
|
|
754
862
|
return rangeDigits({ hour, minute: 0 }, { hour, minute: 59 }, opts);
|
|
755
863
|
}
|
|
@@ -851,7 +959,7 @@ function weekdayQualifier(ir) {
|
|
|
851
959
|
if (quartz) {
|
|
852
960
|
return quartz;
|
|
853
961
|
}
|
|
854
|
-
const segments =
|
|
962
|
+
const segments = orderWeekdaysForDisplay(ir.analyses.segments.weekday);
|
|
855
963
|
return joinList(segments.map(function piece(segment) {
|
|
856
964
|
if (segment.kind === "range") {
|
|
857
965
|
return weekdays[weekdayNumber(segment.bounds[0])].ela + " " + weekdays[weekdayNumber(segment.bounds[1])].ill;
|